From 7598b05a33f86cf05dd6ad34e39b78c95f28d89d Mon Sep 17 00:00:00 2001 From: kellyyeh Date: Fri, 19 Jan 2024 01:35:08 +0000 Subject: [PATCH 1/4] Add dhcpmon submodule and latest updates --- src/dhcpmon/.azure-pipelines/build.yml | 103 +++++++++++++ src/dhcpmon/azure-pipelines.yml | 28 ++++ src/dhcpmon/azurepipelines-coverage.yml | 5 + src/dhcpmon/debian/rules | 4 +- src/dhcpmon/objects.mk | 2 +- src/dhcpmon/src/dhcp_device.cpp | 193 ++++++++++++++++++++++-- src/dhcpmon/src/dhcp_device.h | 24 +++ src/dhcpmon/src/dhcp_mon.cpp | 17 ++- src/dhcpmon/src/main.cpp | 20 ++- src/dhcpmon/src/subdir.mk | 2 +- 10 files changed, 379 insertions(+), 19 deletions(-) create mode 100644 src/dhcpmon/.azure-pipelines/build.yml create mode 100644 src/dhcpmon/azure-pipelines.yml create mode 100644 src/dhcpmon/azurepipelines-coverage.yml diff --git a/src/dhcpmon/.azure-pipelines/build.yml b/src/dhcpmon/.azure-pipelines/build.yml new file mode 100644 index 00000000000..6ff8e94c12c --- /dev/null +++ b/src/dhcpmon/.azure-pipelines/build.yml @@ -0,0 +1,103 @@ +parameters: +- name: 'arch' + type: string +- name: 'pool' + type: object + default: {} +- name: 'containerImage' + type: string +- name: 'codeCoverage' + type: boolean + default: false + +jobs: +- job: + displayName: ${{ parameters.arch }} + timeoutInMinutes: 60 + pool: ${{ parameters.pool }} + + container: + image: ${{ parameters.containerImage }} + + steps: + - ${{ if and(eq(parameters.arch, 'amd64'), parameters.codeCoverage) }}: + - script: | + wget https://packages.microsoft.com/config/ubuntu/20.04/packages-microsoft-prod.deb -O packages-microsoft-prod.deb + sudo dpkg -i packages-microsoft-prod.deb + rm packages-microsoft-prod.deb + sudo apt-get update + sudo apt-get install -y apt-transport-https + sudo apt-get install -y dotnet-sdk-6.0 + displayName: install .Net + - script: | + sudo apt-get update + sudo apt-get install -y \ + libboost-system-dev \ + libboost-thread-dev \ + libboost-serialization-dev \ + libexplain-dev \ + libhiredis-dev \ + libnl-3-dev \ + libnl-route-3-dev \ + libnl-genl-3-dev \ + libnl-nf-3-dev \ + libjsoncpp-dev \ + libevent-dev + displayName: "Install dependencies" + - checkout: self + clean: true + submodules: true + - task: DownloadPipelineArtifact@2 + inputs: + source: specific + project: build + pipeline: Azure.sonic-buildimage.common_libs + runVersion: 'latestFromBranch' + runBranch: 'refs/heads/master' + path: $(Build.ArtifactStagingDirectory)/download + ${{ if eq(parameters.arch, 'amd64') }}: + artifact: common-lib + ${{ else }}: + artifact: common-lib.${{ parameters.arch }} + patterns: | + target/debs/bullseye/libyang-*.deb + target/debs/bullseye/libyang_*.deb + displayName: "Download libyang from common lib" + - script: | + set -ex + sudo dpkg -i $(find ./download -name *.deb) + workingDirectory: $(Build.ArtifactStagingDirectory) + displayName: "Install libyang from common lib" + - task: DownloadPipelineArtifact@2 + inputs: + source: specific + project: build + pipeline: Azure.sonic-swss-common + ${{ if eq(parameters.arch, 'amd64') }}: + artifact: sonic-swss-common + ${{ else }}: + artifact: sonic-swss-common.${{ parameters.arch }} + runVersion: 'latestFromBranch' + runBranch: 'refs/heads/master' + displayName: "Download sonic-swss-common" + - script: | + set -ex + # LIBSWSSCOMMON + sudo dpkg -i libswsscommon_1.0.0_${{ parameters.arch }}.deb + sudo dpkg -i libswsscommon-dev_1.0.0_${{ parameters.arch }}.deb + workingDirectory: $(Pipeline.Workspace)/ + displayName: 'Install libswsscommon package' + - script: | + rm ../*.deb + dpkg-buildpackage -us -uc -b -j$(nproc) + cp ../*.deb $(Build.ArtifactStagingDirectory) + displayName: "Compile sonic dhcpmon" + - publish: $(Build.ArtifactStagingDirectory) + artifact: sonic-dhcpmon.${{ parameters.arch }} + displayName: "Archive dhcpmon debian packages" + - ${{ if and(eq(parameters.arch, 'amd64'), parameters.codeCoverage) }}: + - task: PublishCodeCoverageResults@1 + inputs: + summaryFileLocation: dhcpmon-test-result.xml + pathToSources: $(Build.SourcesDirectory) + codeCoverageTool: 'Cobertura' diff --git a/src/dhcpmon/azure-pipelines.yml b/src/dhcpmon/azure-pipelines.yml new file mode 100644 index 00000000000..59adec7a5c6 --- /dev/null +++ b/src/dhcpmon/azure-pipelines.yml @@ -0,0 +1,28 @@ +# C/C++ with GCC +# Build your C/C++ project with GCC using make. +# Add steps that publish test results, save build artifacts, deploy, and more: +# https://docs.microsoft.com/azure/devops/pipelines/apps/c-cpp/gcc + +trigger: + branches: + include: + - "*" + +jobs: +- template: .azure-pipelines/build.yml + parameters: + arch: amd64 + pool: + vmImage: 'ubuntu-20.04' + codeCoverage: false + containerImage: sonicdev-microsoft.azurecr.io:443/sonic-slave-bullseye:latest +- template: .azure-pipelines/build.yml + parameters: + arch: arm64 + pool: sonicbld-arm64 + containerImage: sonicdev-microsoft.azurecr.io:443/sonic-slave-bullseye-arm64:latest +- template: .azure-pipelines/build.yml + parameters: + arch: armhf + pool: sonicbld-armhf + containerImage: sonicdev-microsoft.azurecr.io:443/sonic-slave-bullseye-armhf:latest diff --git a/src/dhcpmon/azurepipelines-coverage.yml b/src/dhcpmon/azurepipelines-coverage.yml new file mode 100644 index 00000000000..0eeb7083138 --- /dev/null +++ b/src/dhcpmon/azurepipelines-coverage.yml @@ -0,0 +1,5 @@ +coverage: + status: # Code coverage status will be posted to pull requests based on targets defined below. + comments: on # Off by default. When on, details about coverage for each file changed will be posted as a pull request comment. + diff: # Diff coverage is code coverage only for the lines changed in a pull request. + target: 60% # Set this to a desired percentage. Default is 70 percent \ No newline at end of file diff --git a/src/dhcpmon/debian/rules b/src/dhcpmon/debian/rules index 76fc7ea1f83..1386a2a58a8 100755 --- a/src/dhcpmon/debian/rules +++ b/src/dhcpmon/debian/rules @@ -2,8 +2,8 @@ export DEB_BUILD_MAINT_OPTIONS=hardening=+all -DEB_CFLAGS_APPEND=-std=gnu11 -export DEB_CFLAGS_APPEND +DEB_CPPFLAGS_APPEND=-std=gnu11 +export DEB_CPPFLAGS_APPEND %: dh $@ --parallel diff --git a/src/dhcpmon/objects.mk b/src/dhcpmon/objects.mk index dc0d09e5021..966d4815eea 100644 --- a/src/dhcpmon/objects.mk +++ b/src/dhcpmon/objects.mk @@ -1,4 +1,4 @@ USER_OBJS := -LIBS := -levent -lexplain -lswsscommon -pthread -lboost_thread -lboost_system -lhiredis +LIBS := -levent -lexplain -lswsscommon -ljsoncpp -pthread -lboost_thread -lboost_system -lhiredis diff --git a/src/dhcpmon/src/dhcp_device.cpp b/src/dhcpmon/src/dhcp_device.cpp index ac46ef6d135..9796a14dbbc 100644 --- a/src/dhcpmon/src/dhcp_device.cpp +++ b/src/dhcpmon/src/dhcp_device.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -18,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -43,7 +45,8 @@ #define DHCP_OPTIONS_HEADER_SIZE 240 /** Offset of DHCP GIADDR */ #define DHCP_GIADDR_OFFSET 24 -#define CLIENT_IF_PREFIX "Ethernet" +/* STATE_DB DHCP counter table name */ +#define DB_COUNTER_TABLE "DHCP_COUNTER_TABLE|" #define OP_LDHA (BPF_LD | BPF_H | BPF_ABS) /** bpf ldh Abs */ #define OP_LDHI (BPF_LD | BPF_H | BPF_IND) /** bpf ldh Ind */ @@ -57,8 +60,8 @@ std::shared_ptr mConfigDbPtr = std::make_shared ("CONFIG_DB", 0); std::shared_ptr mStateDbPtr = std::make_shared ("STATE_DB", 0); std::shared_ptr mStateDbMuxTablePtr = std::make_shared ( - mStateDbPtr.get(), "HW_MUX_CABLE_TABLE" - ); + mStateDbPtr.get(), "HW_MUX_CABLE_TABLE" +); /* interface to vlan mapping */ std::unordered_map vlan_map; @@ -69,6 +72,15 @@ std::unordered_map portchan_map; /* interface to mgmt port mapping */ std::unordered_map mgmt_map; +/* db counter name array, message type rage [1, 8] */ +std::string db_counter_name[DHCP_MESSAGE_TYPE_COUNT] = { + "Unknown", "Discover", "Offer", "Request", "Decline", "Ack", "Nak", "Release", "Inform" +}; +/* db counter init value in uint64_t */ +uint64_t db_counter[DHCP_MESSAGE_TYPE_COUNT] = {}; + +const std::string init_counter_str; + /** 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)"` @@ -164,29 +176,64 @@ static dhcp_message_type_t monitored_msgs[] = { void update_vlan_mapping(std::shared_ptr db_conn) { auto match_pattern = std::string("VLAN_MEMBER|*"); auto keys = db_conn->keys(match_pattern); + std::unordered_map vlans; 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; + vlans[vlan] = true; syslog(LOG_INFO, "add <%s, %s> into interface vlan map\n", interface.c_str(), vlan.c_str()); + std::string ifname = interface; + initialize_db_counters(ifname); + } + for (auto &itr : vlans) { + std::string ifname = itr.first; + initialize_db_counters(ifname); + } +} + + +/** + * @code clear_counter(std::shared_ptr state_db); + * + * @brief Clear all counter + * + * @param state_db state_db connector pointer + * + */ +void clear_counter(std::shared_ptr state_db) { + std::string match_pattern = DB_COUNTER_TABLE + std::string("*"); + auto keys = state_db->keys(match_pattern); + + for (auto &itr : keys) { + state_db->del(itr); } } + /** update ethernet interface to port-channel map * PORTCHANNEL_MEMBER|PortChannel101|Ethernet112 */ void update_portchannel_mapping(std::shared_ptr db_conn) { auto match_pattern = std::string("PORTCHANNEL_MEMBER|*"); auto keys = db_conn->keys(match_pattern); + std::unordered_map portchannels; 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; + portchannels[portchannel] = true; syslog(LOG_INFO, "add <%s, %s> into interface port-channel map\n", interface.c_str(), portchannel.c_str()); + std::string ifname = interface; + initialize_db_counters(ifname); + } + for (auto &itr : portchannels) { + std::string ifname = itr.first; + initialize_db_counters(ifname); } } @@ -197,6 +244,98 @@ void update_mgmt_mapping() { if (mgmt) { auto name = std::string(mgmt->intf); mgmt_map[name] = name; + initialize_db_counters(name); + } +} + +/** + * @code void gen_counter_json_str() + * + * @brief generate counter json string based on the value in counters array + * + * @return counter json string + */ +std::string gen_counter_json_str(uint64_t *counters) { + std::string init_value; + + init_value.append("{"); + for (int i = 0; i < DHCP_MESSAGE_TYPE_COUNT; i++) { + auto value = std::to_string(counters[i]); + auto json_str = "'" + db_counter_name[i] + "'"+ ":" + "'" + value + "'"; + init_value.append(json_str); + if (i + 1 < DHCP_MESSAGE_TYPE_COUNT) { + init_value.append(","); + } + } + init_value.append("}"); + return init_value; +} + +/** + * @code void increase_db_counter(std::string &ifname) + * + * @brief increase the counter in state_db with interface name + * + * @param ifname interface name + * + * @return none + */ +void initialize_db_counters(std::string &ifname) +{ + auto table_name = DB_COUNTER_TABLE + ifname; + auto init_value = gen_counter_json_str(db_counter); + mStateDbPtr->hset(table_name, "RX", init_value); + mStateDbPtr->hset(table_name, "TX", init_value); +} + +/** + * @code void increase_db_counter(std::string &ifname, uint8_t msg_type, dhcp_packet_direction_t dir) + * + * @brief increase the counter in state_db with count of each DHCP message types + * + * @param ifname interface name + * @param type dhcp message type to be increased in counter + * @param dir dhcp packet direction + * + * @return none + */ +void increase_db_counter(std::string &ifname, uint8_t type, dhcp_packet_direction_t dir) { + if (type >= DHCP_MESSAGE_TYPE_COUNT) { + syslog(LOG_WARNING, "Unexpected message type %d(0x%x)\n", type, type); + type = 0; // treate it as unknown counter + } + std::string table_name = DB_COUNTER_TABLE + ifname; + std::string msg_type = db_counter_name[type]; + auto counters_json = mStateDbPtr->hget(table_name, (dir == DHCP_RX) ? "RX" : "TX"); + if (counters_json == nullptr) { + db_counter[type] = 1; + auto json_string = gen_counter_json_str(db_counter); + mStateDbPtr->hset(table_name, (dir == DHCP_RX) ? "RX" : "TX", json_string); + db_counter[type] = 0; + } else { + Json::Value root; + Json::CharReaderBuilder builder; + Json::StreamWriterBuilder wbuilder; + JSONCPP_STRING err; + + std::replace(counters_json.get()->begin(), counters_json.get()->end(), '\'', '\"'); + auto json_begin = counters_json.get()->c_str(); + auto json_end = json_begin + counters_json.get()->length(); + const std::unique_ptr reader(builder.newCharReader()); + if (reader->parse(json_begin, json_end, &root, &err)) { + if (root.isMember(msg_type)) { + std::string cnt_string = root[msg_type].asString(); + auto cnt = std::stoull(cnt_string) + 1; + root[msg_type] = Json::Value(std::to_string(cnt)); + } else { + root[msg_type] = Json::Value(std::to_string(1)); + } + wbuilder["indentation"] = ""; // whitespace-less output + const std::string document = Json::writeString(wbuilder, root); + mStateDbPtr->hset(table_name, (dir == DHCP_RX) ? "RX" : "TX", document); + } else { + syslog(LOG_WARNING, "failed to parse counter json: %s, %s", json_begin, err.c_str()); + } } } @@ -216,6 +355,7 @@ static uint8_t monitored_msg_sz = sizeof(monitored_msgs) / sizeof(*monitored_msg * * @brief handle the logic related to DHCP option 53 * + * @param src_if Source pyhsical interface name * @param context Device (interface) context * @param dhcp_option pointer to DHCP option buffer space * @param dir packet direction @@ -224,13 +364,22 @@ static uint8_t monitored_msg_sz = sizeof(monitored_msgs) / sizeof(*monitored_msg * * @return none */ -static void handle_dhcp_option_53(dhcp_device_context_t *context, +static void handle_dhcp_option_53(std::string &sock_if, + 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; + std::string context_if(context->intf); + + // count for incomming physical interfaces + if (context_if.compare(sock_if) != 0) { + increase_db_counter(sock_if, dhcp_option[2], dir); + return; + } + switch (dhcp_option[2]) { // DHCP messages send by client @@ -245,6 +394,8 @@ static void handle_dhcp_option_53(dhcp_device_context_t *context, (!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]]++; + // count for device context interfaces (-d -u -m) + increase_db_counter(context_if, dhcp_option[2], dir); } break; // DHCP messages send by server @@ -255,25 +406,33 @@ static void handle_dhcp_option_53(dhcp_device_context_t *context, (!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]]++; + // count for device context interfaces (-d -u -m) + increase_db_counter(context_if, dhcp_option[2], dir); } break; default: syslog(LOG_WARNING, "handle_dhcp_option_53(%s): Unknown DHCP option 53 type %d", context->intf, dhcp_option[2]); + // count for device context interfaces (-d -u -m) + increase_db_counter(context_if, dhcp_option[2], dir); break; } } /** - * @code client_packet_handler(dhcp_device_context_t *context, ssize_t buffer_sz); + * @code client_packet_handler(std::string &sock_if, dhcp_device_context_t *context, uint8_t *buffer, + * ssize_t buffer_sz, dhcp_packet_direction_t dir); * * @brief packet handler to process received rx and tx packets * + * @param sock_if socket interface * @param context pointer to device (interface) context + * @param buffer DHCP packet * @param buffer_sz buffer that stores received packet data + * @param dir DHCP packet direction * * @return none */ -static void client_packet_handler(dhcp_device_context_t *context, uint8_t *buffer, +static void client_packet_handler(std::string &sock_if, 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); @@ -293,7 +452,7 @@ static void client_packet_handler(dhcp_device_context_t *context, uint8_t *buffe 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); + handle_dhcp_option_53(sock_if, context, &dhcp_option[offset], dir, iphdr, dhcphdr); } break; // break while loop since we are only interested in Option 53 } @@ -331,6 +490,7 @@ static dhcp_device_context_t *interface_to_dev_context(std::unordered_mapsecond); } + return find_device_context(devices, ifname); } } return NULL; @@ -366,7 +526,12 @@ static void read_tx_callback(int fd, short event, void *arg) std::string intf(interfaceName); context = find_device_context(devices, intf); if (context) { - client_packet_handler(context, tx_recv_buffer, buffer_sz, DHCP_TX); + client_packet_handler(intf, context, tx_recv_buffer, buffer_sz, DHCP_TX); + } else { + context = interface_to_dev_context(devices, intf); + if (context) { + client_packet_handler(intf, context, tx_recv_buffer, buffer_sz, DHCP_TX); + } } } } @@ -398,10 +563,15 @@ static void read_rx_callback(int fd, short event, void *arg) continue; } std::string intf(interfaceName); - context = interface_to_dev_context(devices, intf); + context = find_device_context(devices, intf); if (context) { - client_packet_handler(context, rx_recv_buffer, buffer_sz, DHCP_RX); - } + client_packet_handler(intf, context, rx_recv_buffer, buffer_sz, DHCP_RX); + } else { + context = interface_to_dev_context(devices, intf); + if (context) { + client_packet_handler(intf, context, rx_recv_buffer, buffer_sz, DHCP_RX); + } + } } } @@ -772,6 +942,7 @@ int dhcp_device_start_capture(size_t snaplen, struct event_base *base, in_addr_t init_recv_buffers(snaplen); + clear_counter(mStateDbPtr); update_vlan_mapping(mConfigDbPtr); update_portchannel_mapping(mConfigDbPtr); update_mgmt_mapping(); diff --git a/src/dhcpmon/src/dhcp_device.h b/src/dhcpmon/src/dhcp_device.h index cd8eab1ee7e..a75f9439580 100644 --- a/src/dhcpmon/src/dhcp_device.h +++ b/src/dhcpmon/src/dhcp_device.h @@ -194,4 +194,28 @@ void dhcp_device_update_snapshot(dhcp_device_context_t *context); */ void dhcp_device_print_status(dhcp_device_context_t *context, dhcp_counters_type_t type); +/** + * @code void increase_db_counter(std::string &ifname) + * + * @brief increase the counter in state_db with interface name + * + * @param ifname interface name + * + * @return none + */ +void initialize_db_counters(std::string &ifname); + +/** + * @code void increase_db_counter(std::string &ifname, uint8_t msg_type, dhcp_packet_direction_t dir) + * + * @brief increase the counter in state_db with count of each DHCP message types + * + * @param ifname interface name + * @param type dhcp message type to be increased in counter + * @param dir dhcp packet direction + * + * @return none + */ +void increase_db_counter(std::string &ifname, uint8_t type, dhcp_packet_direction_t dir); + #endif /* DHCP_DEVICE_H_ */ diff --git a/src/dhcpmon/src/dhcp_mon.cpp b/src/dhcpmon/src/dhcp_mon.cpp index 21b0a363d87..66f2001b6e2 100644 --- a/src/dhcpmon/src/dhcp_mon.cpp +++ b/src/dhcpmon/src/dhcp_mon.cpp @@ -15,6 +15,7 @@ #include "dhcp_mon.h" #include "dhcp_devman.h" +#include "events.h" /** DHCP device/interface state */ typedef struct @@ -42,6 +43,8 @@ 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] = { @@ -97,7 +100,15 @@ static void check_dhcp_relay_health(dhcp_mon_state_t *state_data) { case DHCP_MON_STATUS_UNHEALTHY: if (++state_data->count > dhcp_unhealthy_max_count) { - syslog(LOG_ALERT, state_data->msg, state_data->count * window_interval_sec, context->intf); + 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); } @@ -186,6 +197,8 @@ int dhcp_mon_init(int window_sec, int max_count) break; } + g_events_handle = events_init_publisher("sonic-events-dhcp-relay"); + rv = 0; } while (0); @@ -210,6 +223,8 @@ void dhcp_mon_shutdown() event_free(ev_sigusr1); event_base_free(base); + + events_deinit_publisher(g_events_handle); } /** diff --git a/src/dhcpmon/src/main.cpp b/src/dhcpmon/src/main.cpp index e660bbd908a..bb9e9483684 100644 --- a/src/dhcpmon/src/main.cpp +++ b/src/dhcpmon/src/main.cpp @@ -114,11 +114,13 @@ int main(int argc, char **argv) { int rv = EXIT_FAILURE; int i; + char *endptr; 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; + errno = 0; setlogmask(LOG_UPTO(LOG_INFO)); openlog(basename(argv[0]), LOG_CONS | LOG_PID | LOG_NDELAY, LOG_DAEMON); @@ -150,15 +152,27 @@ int main(int argc, char **argv) i++; break; case 's': - snaplen = atoi(argv[i + 1]); + snaplen = strtol(argv[i + 1], &endptr, 10); + if (errno != 0 || *endptr != '\0') { + fprintf(stderr, "%s: %s: Invalid snap length\n", basename(argv[0]), argv[i + 1]); + usage(basename(argv[0])); + } i += 2; break; case 'w': - window_interval = atoi(argv[i + 1]); + window_interval = strtol(argv[i + 1], &endptr, 10); + if (errno != 0 || *endptr != '\0') { + fprintf(stderr, "%s: %s: Invalid window interval\n", basename(argv[0]), argv[i + 1]); + usage(basename(argv[0])); + } i += 2; break; case 'c': - max_unhealthy_count = atoi(argv[i + 1]); + max_unhealthy_count = strtol(argv[i + 1], &endptr, 10); + if (errno != 0 || *endptr != '\0') { + fprintf(stderr, "%s: %s: Invalid max unhealthy count\n", basename(argv[0]), argv[i + 1]); + usage(basename(argv[0])); + } i += 2; break; case 'D': diff --git a/src/dhcpmon/src/subdir.mk b/src/dhcpmon/src/subdir.mk index dd808aeb720..7c0d9b4a737 100644 --- a/src/dhcpmon/src/subdir.mk +++ b/src/dhcpmon/src/subdir.mk @@ -24,6 +24,6 @@ C_DEPS += \ 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 "$@" "$<" + $(CC) -O3 -g3 -Wall -I/usr/include/swss -c -fmessage-length=0 -MMD -MP -MF"$(@:%.o=%.d)" -MT"$(@)" -o "$@" "$<" @echo 'Finished building: $<' @echo ' ' From 7c2419996233be971834da0d00bf4f89fd2c0435 Mon Sep 17 00:00:00 2001 From: kellyyeh Date: Fri, 19 Jan 2024 01:57:21 +0000 Subject: [PATCH 2/4] Add to gitmodules --- .gitmodules | 3 +++ rules/dhcpmon.dep | 5 +++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/.gitmodules b/.gitmodules index dcc3740421a..261aeecc577 100644 --- a/.gitmodules +++ b/.gitmodules @@ -112,3 +112,6 @@ [submodule "src/dhcprelay"] path = src/dhcprelay url = https://github.com/sonic-net/sonic-dhcp-relay.git +[submodule "src/dhcpmon"] + path = src/dhcpmon + url = https://github.com/sonic-net/sonic-dhcpmon.git diff --git a/rules/dhcpmon.dep b/rules/dhcpmon.dep index cd8a410a8e9..0a72c375349 100644 --- a/rules/dhcpmon.dep +++ b/rules/dhcpmon.dep @@ -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) \ No newline at end of file From 7092333ab3904a11202ce81c2933de5bd9ca97d8 Mon Sep 17 00:00:00 2001 From: kellyyeh Date: Fri, 19 Jan 2024 02:05:34 +0000 Subject: [PATCH 3/4] Update --- src/dhcpmon/.azure-pipelines/build.yml | 103 --- src/dhcpmon/.gitignore | 5 - src/dhcpmon/Makefile | 45 - src/dhcpmon/azure-pipelines.yml | 28 - src/dhcpmon/azurepipelines-coverage.yml | 5 - src/dhcpmon/debian/changelog | 5 - src/dhcpmon/debian/compat | 1 - src/dhcpmon/debian/control | 14 - src/dhcpmon/debian/rules | 9 - src/dhcpmon/objects.mk | 4 - src/dhcpmon/src/dhcp_device.cpp | 1039 ----------------------- src/dhcpmon/src/dhcp_device.h | 221 ----- src/dhcpmon/src/dhcp_devman.cpp | 226 ----- src/dhcpmon/src/dhcp_devman.h | 132 --- src/dhcpmon/src/dhcp_mon.cpp | 285 ------- src/dhcpmon/src/dhcp_mon.h | 55 -- src/dhcpmon/src/main.cpp | 205 ----- src/dhcpmon/src/subdir.mk | 29 - 18 files changed, 2411 deletions(-) delete mode 100644 src/dhcpmon/.azure-pipelines/build.yml delete mode 100644 src/dhcpmon/.gitignore delete mode 100644 src/dhcpmon/Makefile delete mode 100644 src/dhcpmon/azure-pipelines.yml delete mode 100644 src/dhcpmon/azurepipelines-coverage.yml delete mode 100644 src/dhcpmon/debian/changelog delete mode 100644 src/dhcpmon/debian/compat delete mode 100644 src/dhcpmon/debian/control delete mode 100755 src/dhcpmon/debian/rules delete mode 100644 src/dhcpmon/objects.mk delete mode 100644 src/dhcpmon/src/dhcp_device.cpp delete mode 100644 src/dhcpmon/src/dhcp_device.h delete mode 100644 src/dhcpmon/src/dhcp_devman.cpp delete mode 100644 src/dhcpmon/src/dhcp_devman.h delete mode 100644 src/dhcpmon/src/dhcp_mon.cpp delete mode 100644 src/dhcpmon/src/dhcp_mon.h delete mode 100644 src/dhcpmon/src/main.cpp delete mode 100644 src/dhcpmon/src/subdir.mk diff --git a/src/dhcpmon/.azure-pipelines/build.yml b/src/dhcpmon/.azure-pipelines/build.yml deleted file mode 100644 index 6ff8e94c12c..00000000000 --- a/src/dhcpmon/.azure-pipelines/build.yml +++ /dev/null @@ -1,103 +0,0 @@ -parameters: -- name: 'arch' - type: string -- name: 'pool' - type: object - default: {} -- name: 'containerImage' - type: string -- name: 'codeCoverage' - type: boolean - default: false - -jobs: -- job: - displayName: ${{ parameters.arch }} - timeoutInMinutes: 60 - pool: ${{ parameters.pool }} - - container: - image: ${{ parameters.containerImage }} - - steps: - - ${{ if and(eq(parameters.arch, 'amd64'), parameters.codeCoverage) }}: - - script: | - wget https://packages.microsoft.com/config/ubuntu/20.04/packages-microsoft-prod.deb -O packages-microsoft-prod.deb - sudo dpkg -i packages-microsoft-prod.deb - rm packages-microsoft-prod.deb - sudo apt-get update - sudo apt-get install -y apt-transport-https - sudo apt-get install -y dotnet-sdk-6.0 - displayName: install .Net - - script: | - sudo apt-get update - sudo apt-get install -y \ - libboost-system-dev \ - libboost-thread-dev \ - libboost-serialization-dev \ - libexplain-dev \ - libhiredis-dev \ - libnl-3-dev \ - libnl-route-3-dev \ - libnl-genl-3-dev \ - libnl-nf-3-dev \ - libjsoncpp-dev \ - libevent-dev - displayName: "Install dependencies" - - checkout: self - clean: true - submodules: true - - task: DownloadPipelineArtifact@2 - inputs: - source: specific - project: build - pipeline: Azure.sonic-buildimage.common_libs - runVersion: 'latestFromBranch' - runBranch: 'refs/heads/master' - path: $(Build.ArtifactStagingDirectory)/download - ${{ if eq(parameters.arch, 'amd64') }}: - artifact: common-lib - ${{ else }}: - artifact: common-lib.${{ parameters.arch }} - patterns: | - target/debs/bullseye/libyang-*.deb - target/debs/bullseye/libyang_*.deb - displayName: "Download libyang from common lib" - - script: | - set -ex - sudo dpkg -i $(find ./download -name *.deb) - workingDirectory: $(Build.ArtifactStagingDirectory) - displayName: "Install libyang from common lib" - - task: DownloadPipelineArtifact@2 - inputs: - source: specific - project: build - pipeline: Azure.sonic-swss-common - ${{ if eq(parameters.arch, 'amd64') }}: - artifact: sonic-swss-common - ${{ else }}: - artifact: sonic-swss-common.${{ parameters.arch }} - runVersion: 'latestFromBranch' - runBranch: 'refs/heads/master' - displayName: "Download sonic-swss-common" - - script: | - set -ex - # LIBSWSSCOMMON - sudo dpkg -i libswsscommon_1.0.0_${{ parameters.arch }}.deb - sudo dpkg -i libswsscommon-dev_1.0.0_${{ parameters.arch }}.deb - workingDirectory: $(Pipeline.Workspace)/ - displayName: 'Install libswsscommon package' - - script: | - rm ../*.deb - dpkg-buildpackage -us -uc -b -j$(nproc) - cp ../*.deb $(Build.ArtifactStagingDirectory) - displayName: "Compile sonic dhcpmon" - - publish: $(Build.ArtifactStagingDirectory) - artifact: sonic-dhcpmon.${{ parameters.arch }} - displayName: "Archive dhcpmon debian packages" - - ${{ if and(eq(parameters.arch, 'amd64'), parameters.codeCoverage) }}: - - task: PublishCodeCoverageResults@1 - inputs: - summaryFileLocation: dhcpmon-test-result.xml - pathToSources: $(Build.SourcesDirectory) - codeCoverageTool: 'Cobertura' diff --git a/src/dhcpmon/.gitignore b/src/dhcpmon/.gitignore deleted file mode 100644 index 9d09ae6b3f1..00000000000 --- a/src/dhcpmon/.gitignore +++ /dev/null @@ -1,5 +0,0 @@ -debian/* -!debian/changelog -!debian/compat -!debian/control -!debian/rules diff --git a/src/dhcpmon/Makefile b/src/dhcpmon/Makefile deleted file mode 100644 index 4d21f57199f..00000000000 --- a/src/dhcpmon/Makefile +++ /dev/null @@ -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 diff --git a/src/dhcpmon/azure-pipelines.yml b/src/dhcpmon/azure-pipelines.yml deleted file mode 100644 index 59adec7a5c6..00000000000 --- a/src/dhcpmon/azure-pipelines.yml +++ /dev/null @@ -1,28 +0,0 @@ -# C/C++ with GCC -# Build your C/C++ project with GCC using make. -# Add steps that publish test results, save build artifacts, deploy, and more: -# https://docs.microsoft.com/azure/devops/pipelines/apps/c-cpp/gcc - -trigger: - branches: - include: - - "*" - -jobs: -- template: .azure-pipelines/build.yml - parameters: - arch: amd64 - pool: - vmImage: 'ubuntu-20.04' - codeCoverage: false - containerImage: sonicdev-microsoft.azurecr.io:443/sonic-slave-bullseye:latest -- template: .azure-pipelines/build.yml - parameters: - arch: arm64 - pool: sonicbld-arm64 - containerImage: sonicdev-microsoft.azurecr.io:443/sonic-slave-bullseye-arm64:latest -- template: .azure-pipelines/build.yml - parameters: - arch: armhf - pool: sonicbld-armhf - containerImage: sonicdev-microsoft.azurecr.io:443/sonic-slave-bullseye-armhf:latest diff --git a/src/dhcpmon/azurepipelines-coverage.yml b/src/dhcpmon/azurepipelines-coverage.yml deleted file mode 100644 index 0eeb7083138..00000000000 --- a/src/dhcpmon/azurepipelines-coverage.yml +++ /dev/null @@ -1,5 +0,0 @@ -coverage: - status: # Code coverage status will be posted to pull requests based on targets defined below. - comments: on # Off by default. When on, details about coverage for each file changed will be posted as a pull request comment. - diff: # Diff coverage is code coverage only for the lines changed in a pull request. - target: 60% # Set this to a desired percentage. Default is 70 percent \ No newline at end of file diff --git a/src/dhcpmon/debian/changelog b/src/dhcpmon/debian/changelog deleted file mode 100644 index 83b79d6d93b..00000000000 --- a/src/dhcpmon/debian/changelog +++ /dev/null @@ -1,5 +0,0 @@ -sonic-dhcpmon (1.0.0-0) UNRELEASED; urgency=medium - - * Initial release. - - -- Tamer Ahmed Mon, 09 Dec 2019 12:00:00 -0700 diff --git a/src/dhcpmon/debian/compat b/src/dhcpmon/debian/compat deleted file mode 100644 index 48082f72f08..00000000000 --- a/src/dhcpmon/debian/compat +++ /dev/null @@ -1 +0,0 @@ -12 diff --git a/src/dhcpmon/debian/control b/src/dhcpmon/debian/control deleted file mode 100644 index c70c274f19f..00000000000 --- a/src/dhcpmon/debian/control +++ /dev/null @@ -1,14 +0,0 @@ -Source: sonic-dhcpmon -Section: devel -Priority: optional -Maintainer: Tamer Ahmed -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 diff --git a/src/dhcpmon/debian/rules b/src/dhcpmon/debian/rules deleted file mode 100755 index 1386a2a58a8..00000000000 --- a/src/dhcpmon/debian/rules +++ /dev/null @@ -1,9 +0,0 @@ -#!/usr/bin/make -f - -export DEB_BUILD_MAINT_OPTIONS=hardening=+all - -DEB_CPPFLAGS_APPEND=-std=gnu11 -export DEB_CPPFLAGS_APPEND - -%: - dh $@ --parallel diff --git a/src/dhcpmon/objects.mk b/src/dhcpmon/objects.mk deleted file mode 100644 index 966d4815eea..00000000000 --- a/src/dhcpmon/objects.mk +++ /dev/null @@ -1,4 +0,0 @@ -USER_OBJS := - -LIBS := -levent -lexplain -lswsscommon -ljsoncpp -pthread -lboost_thread -lboost_system -lhiredis - diff --git a/src/dhcpmon/src/dhcp_device.cpp b/src/dhcpmon/src/dhcp_device.cpp deleted file mode 100644 index 9796a14dbbc..00000000000 --- a/src/dhcpmon/src/dhcp_device.cpp +++ /dev/null @@ -1,1039 +0,0 @@ -/** - * @file dhcp_device.c - * - * device (interface) module - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#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 -/* STATE_DB DHCP counter table name */ -#define DB_COUNTER_TABLE "DHCP_COUNTER_TABLE|" - -#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 mConfigDbPtr = std::make_shared ("CONFIG_DB", 0); -std::shared_ptr mStateDbPtr = std::make_shared ("STATE_DB", 0); -std::shared_ptr mStateDbMuxTablePtr = std::make_shared ( - mStateDbPtr.get(), "HW_MUX_CABLE_TABLE" -); - -/* interface to vlan mapping */ -std::unordered_map vlan_map; - -/* interface to port-channel mapping */ -std::unordered_map portchan_map; - -/* interface to mgmt port mapping */ -std::unordered_map mgmt_map; - -/* db counter name array, message type rage [1, 8] */ -std::string db_counter_name[DHCP_MESSAGE_TYPE_COUNT] = { - "Unknown", "Discover", "Offer", "Request", "Decline", "Ack", "Nak", "Release", "Inform" -}; -/* db counter init value in uint64_t */ -uint64_t db_counter[DHCP_MESSAGE_TYPE_COUNT] = {}; - -const std::string init_counter_str; - -/** 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 db_conn) { - auto match_pattern = std::string("VLAN_MEMBER|*"); - auto keys = db_conn->keys(match_pattern); - std::unordered_map vlans; - 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; - vlans[vlan] = true; - syslog(LOG_INFO, "add <%s, %s> into interface vlan map\n", interface.c_str(), vlan.c_str()); - std::string ifname = interface; - initialize_db_counters(ifname); - } - for (auto &itr : vlans) { - std::string ifname = itr.first; - initialize_db_counters(ifname); - } -} - - -/** - * @code clear_counter(std::shared_ptr state_db); - * - * @brief Clear all counter - * - * @param state_db state_db connector pointer - * - */ -void clear_counter(std::shared_ptr state_db) { - std::string match_pattern = DB_COUNTER_TABLE + std::string("*"); - auto keys = state_db->keys(match_pattern); - - for (auto &itr : keys) { - state_db->del(itr); - } -} - - -/** update ethernet interface to port-channel map - * PORTCHANNEL_MEMBER|PortChannel101|Ethernet112 - */ -void update_portchannel_mapping(std::shared_ptr db_conn) { - auto match_pattern = std::string("PORTCHANNEL_MEMBER|*"); - auto keys = db_conn->keys(match_pattern); - std::unordered_map portchannels; - 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; - portchannels[portchannel] = true; - syslog(LOG_INFO, "add <%s, %s> into interface port-channel map\n", interface.c_str(), portchannel.c_str()); - std::string ifname = interface; - initialize_db_counters(ifname); - } - for (auto &itr : portchannels) { - std::string ifname = itr.first; - initialize_db_counters(ifname); - } -} - -/** 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; - initialize_db_counters(name); - } -} - -/** - * @code void gen_counter_json_str() - * - * @brief generate counter json string based on the value in counters array - * - * @return counter json string - */ -std::string gen_counter_json_str(uint64_t *counters) { - std::string init_value; - - init_value.append("{"); - for (int i = 0; i < DHCP_MESSAGE_TYPE_COUNT; i++) { - auto value = std::to_string(counters[i]); - auto json_str = "'" + db_counter_name[i] + "'"+ ":" + "'" + value + "'"; - init_value.append(json_str); - if (i + 1 < DHCP_MESSAGE_TYPE_COUNT) { - init_value.append(","); - } - } - init_value.append("}"); - return init_value; -} - -/** - * @code void increase_db_counter(std::string &ifname) - * - * @brief increase the counter in state_db with interface name - * - * @param ifname interface name - * - * @return none - */ -void initialize_db_counters(std::string &ifname) -{ - auto table_name = DB_COUNTER_TABLE + ifname; - auto init_value = gen_counter_json_str(db_counter); - mStateDbPtr->hset(table_name, "RX", init_value); - mStateDbPtr->hset(table_name, "TX", init_value); -} - -/** - * @code void increase_db_counter(std::string &ifname, uint8_t msg_type, dhcp_packet_direction_t dir) - * - * @brief increase the counter in state_db with count of each DHCP message types - * - * @param ifname interface name - * @param type dhcp message type to be increased in counter - * @param dir dhcp packet direction - * - * @return none - */ -void increase_db_counter(std::string &ifname, uint8_t type, dhcp_packet_direction_t dir) { - if (type >= DHCP_MESSAGE_TYPE_COUNT) { - syslog(LOG_WARNING, "Unexpected message type %d(0x%x)\n", type, type); - type = 0; // treate it as unknown counter - } - std::string table_name = DB_COUNTER_TABLE + ifname; - std::string msg_type = db_counter_name[type]; - auto counters_json = mStateDbPtr->hget(table_name, (dir == DHCP_RX) ? "RX" : "TX"); - if (counters_json == nullptr) { - db_counter[type] = 1; - auto json_string = gen_counter_json_str(db_counter); - mStateDbPtr->hset(table_name, (dir == DHCP_RX) ? "RX" : "TX", json_string); - db_counter[type] = 0; - } else { - Json::Value root; - Json::CharReaderBuilder builder; - Json::StreamWriterBuilder wbuilder; - JSONCPP_STRING err; - - std::replace(counters_json.get()->begin(), counters_json.get()->end(), '\'', '\"'); - auto json_begin = counters_json.get()->c_str(); - auto json_end = json_begin + counters_json.get()->length(); - const std::unique_ptr reader(builder.newCharReader()); - if (reader->parse(json_begin, json_end, &root, &err)) { - if (root.isMember(msg_type)) { - std::string cnt_string = root[msg_type].asString(); - auto cnt = std::stoull(cnt_string) + 1; - root[msg_type] = Json::Value(std::to_string(cnt)); - } else { - root[msg_type] = Json::Value(std::to_string(1)); - } - wbuilder["indentation"] = ""; // whitespace-less output - const std::string document = Json::writeString(wbuilder, root); - mStateDbPtr->hset(table_name, (dir == DHCP_RX) ? "RX" : "TX", document); - } else { - syslog(LOG_WARNING, "failed to parse counter json: %s, %s", json_begin, err.c_str()); - } - } -} - -dhcp_device_context_t *find_device_context(std::unordered_map *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 src_if Source pyhsical interface name - * @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(std::string &sock_if, - 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; - std::string context_if(context->intf); - - // count for incomming physical interfaces - if (context_if.compare(sock_if) != 0) { - increase_db_counter(sock_if, dhcp_option[2], dir); - return; - } - - 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]]++; - // count for device context interfaces (-d -u -m) - increase_db_counter(context_if, dhcp_option[2], dir); - } - 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]]++; - // count for device context interfaces (-d -u -m) - increase_db_counter(context_if, dhcp_option[2], dir); - } - break; - default: - syslog(LOG_WARNING, "handle_dhcp_option_53(%s): Unknown DHCP option 53 type %d", context->intf, dhcp_option[2]); - // count for device context interfaces (-d -u -m) - increase_db_counter(context_if, dhcp_option[2], dir); - break; - } -} - -/** - * @code client_packet_handler(std::string &sock_if, dhcp_device_context_t *context, uint8_t *buffer, - * ssize_t buffer_sz, dhcp_packet_direction_t dir); - * - * @brief packet handler to process received rx and tx packets - * - * @param sock_if socket interface - * @param context pointer to device (interface) context - * @param buffer DHCP packet - * @param buffer_sz buffer that stores received packet data - * @param dir DHCP packet direction - * - * @return none - */ -static void client_packet_handler(std::string &sock_if, 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(sock_if, 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; - } - } - } -} - -static dhcp_device_context_t *interface_to_dev_context(std::unordered_map *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 find_device_context(devices, ifname); - } - } - 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 *)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(intf, context, tx_recv_buffer, buffer_sz, DHCP_TX); - } else { - context = interface_to_dev_context(devices, intf); - if (context) { - client_packet_handler(intf, 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 *)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 = find_device_context(devices, intf); - if (context) { - client_packet_handler(intf, context, rx_recv_buffer, buffer_sz, DHCP_RX); - } else { - context = interface_to_dev_context(devices, intf); - if (context) { - client_packet_handler(intf, 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: %*" PRIu64 "/%*" PRIu64 ", Offer: %*" PRIu64 "/%*" PRIu64 - ", Request: %*" PRIu64 "/%*" PRIu64 ", ACK: %*" PRIu64 "/%*" PRIu64 "\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); - - clear_counter(mStateDbPtr); - 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]); - } -} diff --git a/src/dhcpmon/src/dhcp_device.h b/src/dhcpmon/src/dhcp_device.h deleted file mode 100644 index a75f9439580..00000000000 --- a/src/dhcpmon/src/dhcp_device.h +++ /dev/null @@ -1,221 +0,0 @@ -/** - * @file dhcp_device.h - * - * device (interface) module - */ - -#ifndef DHCP_DEVICE_H_ -#define DHCP_DEVICE_H_ - -#include -#include -#include -#include - -#include -#include -#include - -extern bool dual_tor_sock; -extern std::unordered_map 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); - -/** - * @code void increase_db_counter(std::string &ifname) - * - * @brief increase the counter in state_db with interface name - * - * @param ifname interface name - * - * @return none - */ -void initialize_db_counters(std::string &ifname); - -/** - * @code void increase_db_counter(std::string &ifname, uint8_t msg_type, dhcp_packet_direction_t dir) - * - * @brief increase the counter in state_db with count of each DHCP message types - * - * @param ifname interface name - * @param type dhcp message type to be increased in counter - * @param dir dhcp packet direction - * - * @return none - */ -void increase_db_counter(std::string &ifname, uint8_t type, dhcp_packet_direction_t dir); - -#endif /* DHCP_DEVICE_H_ */ diff --git a/src/dhcpmon/src/dhcp_devman.cpp b/src/dhcpmon/src/dhcp_devman.cpp deleted file mode 100644 index 0fa490d138b..00000000000 --- a/src/dhcpmon/src/dhcp_devman.cpp +++ /dev/null @@ -1,226 +0,0 @@ -/** - * @file dhcp_devman.c - * - * Device (interface) manager - */ -#include -#include -#include -#include -#include - -#include "dhcp_devman.h" - -/** Prefix appended to Aggregation device */ -#define AGG_DEV_PREFIX "Agg-" - -/** intfs map of interfaces */ -std::unordered_map 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); - } -} diff --git a/src/dhcpmon/src/dhcp_devman.h b/src/dhcpmon/src/dhcp_devman.h deleted file mode 100644 index d1c80cf30e9..00000000000 --- a/src/dhcpmon/src/dhcp_devman.h +++ /dev/null @@ -1,132 +0,0 @@ -/** - * @file dhcp_devman.h - * - * Device (interface) manager - */ - -#ifndef DHCP_DEVMAN_H_ -#define DHCP_DEVMAN_H_ - -#include -#include -#include - -#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_ */ diff --git a/src/dhcpmon/src/dhcp_mon.cpp b/src/dhcpmon/src/dhcp_mon.cpp deleted file mode 100644 index 66f2001b6e2..00000000000 --- a/src/dhcpmon/src/dhcp_mon.cpp +++ /dev/null @@ -1,285 +0,0 @@ -/** - * @file dhcp_mon.c - * - * @brief dhcp relay monitor module - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#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); -} diff --git a/src/dhcpmon/src/dhcp_mon.h b/src/dhcpmon/src/dhcp_mon.h deleted file mode 100644 index 5bae01f5962..00000000000 --- a/src/dhcpmon/src/dhcp_mon.h +++ /dev/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_ */ diff --git a/src/dhcpmon/src/main.cpp b/src/dhcpmon/src/main.cpp deleted file mode 100644 index bb9e9483684..00000000000 --- a/src/dhcpmon/src/main.cpp +++ /dev/null @@ -1,205 +0,0 @@ -/** - * @file main.c - * - * @brief: Main entry point for dhcpmon utility. - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#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 {-iu }+ -im [-u ]" - "[-w ] [-c ] [-s ] [-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; - char *endptr; - 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; - errno = 0; - - 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 = strtol(argv[i + 1], &endptr, 10); - if (errno != 0 || *endptr != '\0') { - fprintf(stderr, "%s: %s: Invalid snap length\n", basename(argv[0]), argv[i + 1]); - usage(basename(argv[0])); - } - i += 2; - break; - case 'w': - window_interval = strtol(argv[i + 1], &endptr, 10); - if (errno != 0 || *endptr != '\0') { - fprintf(stderr, "%s: %s: Invalid window interval\n", basename(argv[0]), argv[i + 1]); - usage(basename(argv[0])); - } - i += 2; - break; - case 'c': - max_unhealthy_count = strtol(argv[i + 1], &endptr, 10); - if (errno != 0 || *endptr != '\0') { - fprintf(stderr, "%s: %s: Invalid max unhealthy count\n", basename(argv[0]), argv[i + 1]); - usage(basename(argv[0])); - } - 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; -} diff --git a/src/dhcpmon/src/subdir.mk b/src/dhcpmon/src/subdir.mk deleted file mode 100644 index 7c0d9b4a737..00000000000 --- a/src/dhcpmon/src/subdir.mk +++ /dev/null @@ -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/usr/include/swss -c -fmessage-length=0 -MMD -MP -MF"$(@:%.o=%.d)" -MT"$(@)" -o "$@" "$<" - @echo 'Finished building: $<' - @echo ' ' From be163d8ca3dd772328424d364546e79e6d9a46a5 Mon Sep 17 00:00:00 2001 From: kellyyeh Date: Fri, 19 Jan 2024 02:06:59 +0000 Subject: [PATCH 4/4] Add submodule --- src/dhcpmon | 1 + 1 file changed, 1 insertion(+) create mode 160000 src/dhcpmon diff --git a/src/dhcpmon b/src/dhcpmon new file mode 160000 index 00000000000..fc20a97ba2e --- /dev/null +++ b/src/dhcpmon @@ -0,0 +1 @@ +Subproject commit fc20a97ba2eba753974ea95d504d130093030596