From 563ad1bd213fe82c45c77a6ae1f80ef9cff276d9 Mon Sep 17 00:00:00 2001 From: armatusmiles <akurotych@gmail.com> Date: Fri, 11 Jan 2019 21:50:22 +0700 Subject: [PATCH] [+] dap_network_monitor with unit tests --- .travis.yml | 1 + core/unix/CMakeLists.txt | 2 + core/unix/dap_cpu_monitor.c | 5 - core/unix/dap_network_monitor.c | 185 +++++++++++++++++++++ core/unix/dap_network_monitor.h | 74 ++++++++- core/unix/dap_process_manager.h | 24 +++ test/core/CMakeLists.txt | 2 +- test/core/main.c | 4 + test/core/unix/dap_network_monitor_test.c | 188 ++++++++++++++++++++++ test/core/unix/dap_network_monitor_test.h | 4 + test/libdap-test | 2 +- 11 files changed, 483 insertions(+), 8 deletions(-) create mode 100644 test/core/unix/dap_network_monitor_test.c create mode 100644 test/core/unix/dap_network_monitor_test.h diff --git a/.travis.yml b/.travis.yml index 3e59dc6..f4b36a0 100644 --- a/.travis.yml +++ b/.travis.yml @@ -23,4 +23,5 @@ addons: - ubuntu-toolchain-r-test packages: - gcc-5 + - network-manager diff --git a/core/unix/CMakeLists.txt b/core/unix/CMakeLists.txt index 7d4465b..18c0868 100644 --- a/core/unix/CMakeLists.txt +++ b/core/unix/CMakeLists.txt @@ -7,4 +7,6 @@ file(GLOB CORE_UNIX_HEADERS *.h) add_library(${PROJECT_NAME} STATIC ${CORE_UNIX_SRCS} ${CORE_UNIX_HEADERS}) +target_link_libraries(${PROJECT_NAME} dap_core pthread) + target_include_directories(dap_core_unix INTERFACE .) diff --git a/core/unix/dap_cpu_monitor.c b/core/unix/dap_cpu_monitor.c index 1ce77a5..749b2ec 100644 --- a/core/unix/dap_cpu_monitor.c +++ b/core/unix/dap_cpu_monitor.c @@ -95,8 +95,6 @@ dap_cpu_stats_t dap_cpu_get_stats() _cpu_old_stats[i].idle_time, _cpu_stats.cpus[i].total_time, _cpu_old_stats[i].total_time); - - // log_it(L_WARNING, "CPU %d %f", i, _cpu_stats.cpus[i].load); } _cpu_stats.cpu_summary.load = _calculate_load(_cpu_stats.cpu_summary.idle_time, @@ -104,9 +102,6 @@ dap_cpu_stats_t dap_cpu_get_stats() _cpu_stats.cpu_summary.total_time, _cpu_summary_old.total_time); - // log_it(L_WARNING, "%f", _cpu_stats.cpu_summary.load); - - memcpy(&_cpu_summary_old, &_cpu_stats.cpu_summary, sizeof (dap_cpu_t)); memcpy(_cpu_old_stats, _cpu_stats.cpus, diff --git a/core/unix/dap_network_monitor.c b/core/unix/dap_network_monitor.c index e69de29..d84dfe7 100644 --- a/core/unix/dap_network_monitor.c +++ b/core/unix/dap_network_monitor.c @@ -0,0 +1,185 @@ +#include <linux/netlink.h> +#include <pthread.h> +#include <netinet/in.h> +#include <string.h> +#include <unistd.h> +#include <sys/socket.h> +#include <arpa/inet.h> + + +#include "dap_network_monitor.h" +#include "dap_common.h" + + +#define LOG_TAG "dap_network_monitor" + +static struct { + int socket; + pthread_t thread; + dap_network_monitor_notification_callback_t callback; +} _net_notification; + +static void* network_monitor_worker(void *arg); + +int dap_network_monitor_init(dap_network_monitor_notification_callback_t cb) +{ + memset((void*)&_net_notification, 0, sizeof(_net_notification)); + + if ((_net_notification.socket = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE)) == -1) { + log_it(L_ERROR, "Can't open notification socket"); + return -1; + } + + struct sockaddr_nl addr = {0}; + addr.nl_family = AF_NETLINK; + addr.nl_groups = RTMGRP_IPV4_IFADDR | RTMGRP_IPV4_ROUTE; + if (bind(_net_notification.socket, (struct sockaddr *)&addr, sizeof(addr)) == -1) { + log_it(L_ERROR, "Can't bind notification socket"); + return -2; + } + + pthread_barrier_t barrier; + + pthread_barrier_init(&barrier, NULL, 2); + if(pthread_create(&_net_notification.thread, NULL, network_monitor_worker, &barrier) != 0) { + log_it(L_ERROR, "Error create notification thread"); + return -3; + } + + pthread_barrier_wait(&barrier); + + pthread_barrier_destroy(&barrier); + + _net_notification.callback = cb; + log_it(L_DEBUG, "dap_network_monitor was initialized"); + return 0; +} + +void dap_network_monitor_deinit(void) +{ + if(_net_notification.socket == 0 || _net_notification.socket == -1) { + log_it(L_ERROR, "Network monitor not be inited"); + return; + } + close(_net_notification.socket); + pthread_cancel(_net_notification.thread); + pthread_join(_net_notification.thread, NULL); +} + +static void _ip_addr_msg_handler(struct nlmsghdr *nlh, + dap_network_notification_t* result) +{ + struct ifaddrmsg *ifa = (struct ifaddrmsg *)NLMSG_DATA(nlh); + struct rtattr *rth = IFA_RTA(ifa); + size_t rtl = IFA_PAYLOAD(nlh); + for (; rtl && RTA_OK(rth, rtl); rth = RTA_NEXT(rth,rtl)) { + char *inet_str = inet_ntoa(*((struct in_addr *)RTA_DATA(rth))); + + if (rth->rta_type != IFA_LOCAL) continue; + + /* fill result */ + result->addr.ip = htonl(*((uint32_t *)RTA_DATA(rth))); + strcpy(result->addr.s_ip, inet_str); + if_indextoname(ifa->ifa_index, result->addr.interface_name); + } +} + +static void _route_msg_handler(struct nlmsghdr *nlh, + dap_network_notification_t* result, + int received_bytes) +{ + struct rtmsg *route_entry; /* This struct represent a route entry + in the routing table */ + struct rtattr *route_attribute; /* This struct contain route + attributes (route type) */ + int route_attribute_len = 0; + + route_attribute_len = RTM_PAYLOAD(nlh); + + for ( ; NLMSG_OK(nlh, received_bytes); \ + nlh = NLMSG_NEXT(nlh, received_bytes)) + { + /* Get the route data */ + route_entry = (struct rtmsg *) NLMSG_DATA(nlh); + + result->route.netmask = route_entry->rtm_dst_len; + result->route.protocol = route_entry->rtm_protocol; + + /* Get attributes of route_entry */ + route_attribute = (struct rtattr *) RTM_RTA(route_entry); + + /* Get the route atttibutes len */ + route_attribute_len = RTM_PAYLOAD(nlh); + /* Loop through all attributes */ + for ( ; RTA_OK(route_attribute, route_attribute_len); \ + route_attribute = RTA_NEXT(route_attribute, route_attribute_len)) + { + /* Get the destination address */ + if (route_attribute->rta_type == RTA_DST) + { + result->route.destination_address = htonl(*(uint32_t*)RTA_DATA(route_attribute)); + + inet_ntop(AF_INET, RTA_DATA(route_attribute), + result->route.s_destination_address, + sizeof(result->route.s_destination_address)); + } + /* Get the gateway (Next hop) */ + if (route_attribute->rta_type == RTA_GATEWAY) + { + result->route.gateway_address = htonl(*(uint32_t*)RTA_DATA(route_attribute)); +; + inet_ntop(AF_INET, RTA_DATA(route_attribute), + result->route.s_gateway_address, + sizeof(result->route.s_gateway_address)); + } + } + } + +} + +static void clear_results(dap_network_notification_t* cb_result) { + bzero(cb_result, sizeof (dap_network_notification_t)); + cb_result->route.destination_address = (uint64_t) -1; + cb_result->route.gateway_address = (uint64_t) -1; +} + +static void* network_monitor_worker(void *arg) +{ + pthread_barrier_t *barrier = (pthread_barrier_t *)arg; + log_it(L_DEBUG, "Network monitor worker started"); + if (_net_notification.socket == -1) { + log_it(L_ERROR, "Net socket not running. Can't start worker"); + return NULL; + } + + char buffer[4096]; + dap_network_notification_t callback_result; + + struct nlmsghdr *nlh = (struct nlmsghdr *)buffer; + int len; + + pthread_barrier_wait(barrier); + + while ((len = recv(_net_notification.socket, nlh, sizeof(buffer), 0)) > 0) { + for (; (NLMSG_OK(nlh, len)) && (nlh->nlmsg_type != NLMSG_DONE); nlh = NLMSG_NEXT(nlh, len)) { + + clear_results(&callback_result); + + callback_result.type = nlh->nlmsg_type; + if (nlh->nlmsg_type == RTM_NEWADDR || nlh->nlmsg_type == RTM_DELADDR) { + _ip_addr_msg_handler(nlh, &callback_result); + } else if(nlh->nlmsg_type == RTM_NEWROUTE || nlh->nlmsg_type == RTM_DELROUTE) { + _route_msg_handler(nlh, &callback_result, len); + } else { + log_it(L_DEBUG, "Not supported msg type %d", nlh->nlmsg_type); + continue; + } + + if (_net_notification.callback) { + _net_notification.callback(callback_result); + } else { + log_it(L_ERROR, "callback is NULL"); + } + } + } +} diff --git a/core/unix/dap_network_monitor.h b/core/unix/dap_network_monitor.h index b0ae8cb..b81e588 100644 --- a/core/unix/dap_network_monitor.h +++ b/core/unix/dap_network_monitor.h @@ -1 +1,73 @@ -#include "linux/netlink.h" +/* + * Authors: + * Anatolii Kurotych <akurotych@gmail.com> + * DeM Labs Inc. https://demlabs.net + * DeM Labs Open source community https://github.com/demlabsinc + * Copyright (c) 2017-2019 + * All rights reserved. + + This file is part of DAP (Deus Applications Prototypes) the open source project + + DAP (Deus Applicaions Prototypes) is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + DAP is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with any DAP based project. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <stdint.h> +#include <stdbool.h> +#include <net/if.h> +#include <linux/rtnetlink.h> + +#define MAX_IP_STR_LEN 15 + +typedef enum { + // like in rtnetlink defines + IP_ADDR_ADD = RTM_NEWADDR, + IP_ADDR_REMOVE, + IP_ROUTE_ADD = RTM_NEWROUTE, + IP_ROUTE_REMOVE +} dap_network_monitor_notification_type_t; + +typedef struct { + dap_network_monitor_notification_type_t type; + union { + struct { + char interface_name[IF_NAMESIZE]; + char s_ip[MAX_IP_STR_LEN + 1]; + uint32_t ip; // inet_ntoa(*((struct in_addr *)&ipaddr)) for cast to char* + } addr; // for IP_ADDR_ADD, IP_ADDR_REMOVE + struct { + uint64_t destination_address; // 64 bit for checking -1 like not filled variable + char s_destination_address[MAX_IP_STR_LEN + 1]; + uint64_t gateway_address; + char s_gateway_address[MAX_IP_STR_LEN + 1]; + uint8_t protocol; + uint8_t netmask; + } route; // for IP_ROUTE_ADD, IP_ROUTE_REMOVE + }; +} dap_network_notification_t; + +typedef void (*dap_network_monitor_notification_callback_t) + (const dap_network_notification_t notification); + +/** + * @brief dap_network_monitor_init + * @param callback + * @details starts network monitorting + * @return 0 if successful + */ +int dap_network_monitor_init(dap_network_monitor_notification_callback_t callback); + +/** + * @brief dap_network_monitor_deinit + */ +void dap_network_monitor_deinit(void); diff --git a/core/unix/dap_process_manager.h b/core/unix/dap_process_manager.h index 2fb47d8..b35bfeb 100755 --- a/core/unix/dap_process_manager.h +++ b/core/unix/dap_process_manager.h @@ -1,3 +1,27 @@ +/* + * Authors: + * Anatolii Kurotych <akurotych@gmail.com> + * DeM Labs Inc. https://demlabs.net + * DeM Labs Open source community https://github.com/demlabsinc + * Copyright (c) 2017-2019 + * All rights reserved. + + This file is part of DAP (Deus Applications Prototypes) the open source project + + DAP (Deus Applicaions Prototypes) is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + DAP is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with any DAP based project. If not, see <http://www.gnu.org/licenses/>. +*/ + #ifdef __linux__ #include <stdbool.h> diff --git a/test/core/CMakeLists.txt b/test/core/CMakeLists.txt index 1704153..314e96e 100755 --- a/test/core/CMakeLists.txt +++ b/test/core/CMakeLists.txt @@ -10,7 +10,7 @@ endif() add_executable(${PROJECT_NAME} ${SRCS} ${PLATROFM_DEP_SRC}) -target_link_libraries(core_test dap_test dap_core) +target_link_libraries(core_test dap_test dap_core pthread) add_test( NAME core-test diff --git a/test/core/main.c b/test/core/main.c index 6571276..53ec99e 100755 --- a/test/core/main.c +++ b/test/core/main.c @@ -1,7 +1,9 @@ #include "dap_config_test.h" #include "dap_common_test.h" +#include "dap_network_monitor_test.h" #include "dap_common.h" + int main(void) { // switch off debug info from library set_log_level(L_CRITICAL); @@ -10,7 +12,9 @@ int main(void) { #ifdef __linux__ #include "dap_process_mem_test.h" #include "dap_cpu_monitor_test.h" +#include "dap_network_monitor.h" dap_process_mem_test_run(); dap_cpu_monitor_test_run(); + dap_network_monitor_test_run(); #endif } diff --git a/test/core/unix/dap_network_monitor_test.c b/test/core/unix/dap_network_monitor_test.c new file mode 100644 index 0000000..07d7d92 --- /dev/null +++ b/test/core/unix/dap_network_monitor_test.c @@ -0,0 +1,188 @@ +#include <arpa/inet.h> +#include "linux/rtnetlink.h" + +#include "dap_network_monitor.h" +#include "dap_network_monitor_test.h" + +enum events { + NEW_INTERFACE_EV, + NEW_GATEWAY_EV, + REMOVE_INTERFACE_EV, + REMOVE_GATEWAY_EV, + REMOVE_ROUTE_EV +}; + +#define COUNT_TEST_EVENT_CASES 5 + + +static dap_network_notification_t _test_event_cases[COUNT_TEST_EVENT_CASES]; + + +static bool list_events_done[COUNT_TEST_EVENT_CASES] = {0}; + +void _network_callback(const dap_network_notification_t result) +{ + if(result.type == IP_ADDR_ADD || result.type == IP_ADDR_REMOVE) + { + dap_test_msg("Interface %s %s has IP address %s", + result.addr.interface_name, (result.type == IP_ADDR_ADD ? "now" : "no longer"), + result.addr.s_ip); + enum events event; + if(result.type == IP_ADDR_ADD) { + event = NEW_INTERFACE_EV; + } else { + event = REMOVE_INTERFACE_EV; + } + + dap_test_msg("Checking %s" , (event == NEW_INTERFACE_EV ? + "add new interface callback" : "remove interface callback")); + + dap_assert(result.addr.ip == _test_event_cases[event].addr.ip, + "Check dest ip"); + + dap_assert(dap_str_equals(result.addr.s_ip, _test_event_cases[event].addr.s_ip), + "Check dest str ip"); + + dap_assert(dap_str_equals(result.addr.interface_name, + _test_event_cases[event].addr.interface_name), + "Check interface name"); + + list_events_done[event] = true; + + } else if(result.type == IP_ROUTE_ADD || result.type == IP_ROUTE_REMOVE) { + + if (result.type == IP_ROUTE_REMOVE) { + + if(result.route.destination_address == _test_event_cases[REMOVE_GATEWAY_EV].route.gateway_address) { + dap_pass_msg("Gateway addr removed"); + dap_assert(dap_str_equals(result.route.s_destination_address, + _test_event_cases[REMOVE_GATEWAY_EV].route.s_gateway_address), + "Check gateway str ip"); + + dap_assert(result.route.protocol == _test_event_cases[REMOVE_GATEWAY_EV].route.protocol, + "Check protocol"); + + list_events_done[REMOVE_GATEWAY_EV] = true; + } else if(result.route.destination_address == + _test_event_cases[REMOVE_ROUTE_EV].route.destination_address) { + dap_pass_msg("Destination address removed"); + + dap_assert(dap_str_equals(result.route.s_destination_address, + _test_event_cases[REMOVE_ROUTE_EV].route.s_destination_address), + "Check dest str ip"); + + dap_assert(result.route.protocol == _test_event_cases[REMOVE_ROUTE_EV].route.protocol, + "Check protocol"); + + list_events_done[REMOVE_ROUTE_EV] = true; + } + +// dap_test_msg("Deleting route to destination --> %s/%d proto %d and gateway %s\n", +// result.route.s_destination_address, +// result.route.netmask, +// result.route.protocol, +// result.route.s_gateway_address); + + } else if (result.type == IP_ROUTE_ADD) { + if(result.route.gateway_address != -1) { // gateway address is present + dap_test_msg("Checking new gateway addr"); + dap_assert(result.route.gateway_address == + _test_event_cases[NEW_GATEWAY_EV].route.gateway_address, + "Check gateway ip"); + + dap_assert(dap_str_equals(result.route.s_gateway_address, + _test_event_cases[NEW_GATEWAY_EV].route.s_gateway_address), + "Check gateway str ip"); + + dap_assert(result.route.protocol == _test_event_cases[NEW_GATEWAY_EV].route.protocol, + "Check protocol"); + + list_events_done[NEW_GATEWAY_EV] = true; + } +// dap_test_msg("Adding route to destination --> %s/%d proto %d and gateway %s\n", +// result.route.s_destination_address, +// result.route.netmask, +// result.route.protocol, +// result.route.s_gateway_address); + } + } +} + + +static void init_test_case() +{ + bzero(_test_event_cases, sizeof (_test_event_cases)); + + dap_network_notification_t * res; + + // new_interface + res = &_test_event_cases[NEW_INTERFACE_EV]; + res->type = IP_ADDR_ADD; + strcpy(res->addr.s_ip, "10.1.0.111"); + strcpy(res->addr.interface_name, "tun10"); + res->addr.ip = 167837807; + + // new_gateway + res = &_test_event_cases[NEW_GATEWAY_EV]; + res->type = IP_ROUTE_ADD; + strcpy(res->route.s_gateway_address, "10.1.0.1"); + res->route.gateway_address = 167837697; + res->route.protocol = RTPROT_STATIC; + + res = &_test_event_cases[REMOVE_GATEWAY_EV]; + res->type = IP_ROUTE_REMOVE; + strcpy(res->route.s_gateway_address, "10.1.0.1"); + res->route.gateway_address = 167837697; + res->route.protocol = RTPROT_STATIC; + + + // remove interface + res = &_test_event_cases[REMOVE_INTERFACE_EV]; + res->type = IP_ADDR_REMOVE; + strcpy(res->addr.s_ip, "10.1.0.111"); + strcpy(res->addr.interface_name, "tun10"); + res->addr.ip = 167837807; + + // remote route + res = &_test_event_cases[REMOVE_ROUTE_EV]; + res->type = IP_ROUTE_REMOVE; + strcpy(res->route.s_destination_address, "10.1.0.111"); + res->route.destination_address = 167837807; + res->route.protocol = RTPROT_KERNEL; +} + +static void cleanup_test_case() +{ + +} + +void dap_network_monitor_test_run(void) +{ + dap_print_module_name("dap_network_monitor"); + + init_test_case(); + + dap_network_monitor_init(_network_callback); + + const char *add_test_interfece = "nmcli connection add type tun con-name " + "DiveVPNTest autoconnect false ifname tun10 " + "mode tun ip4 10.1.0.111 gw4 10.1.0.1"; + const char *up_test_interfece = "nmcli connection up DiveVPNTest"; + const char *down_test_interfece = "nmcli connection down DiveVPNTest"; + const char *delete_test_interfece = "nmcli connection delete DiveVPNTest 2> /dev/null"; + + system(delete_test_interfece); + system(add_test_interfece); + system(up_test_interfece); + system(down_test_interfece); + system(delete_test_interfece); + + for(int i = 0; i < COUNT_TEST_EVENT_CASES; i++) { + if(list_events_done[i] == false) { + dap_fail("Not all events were processed"); + } + } + + dap_network_monitor_deinit(); + cleanup_test_case(); +} diff --git a/test/core/unix/dap_network_monitor_test.h b/test/core/unix/dap_network_monitor_test.h new file mode 100644 index 0000000..1dae522 --- /dev/null +++ b/test/core/unix/dap_network_monitor_test.h @@ -0,0 +1,4 @@ +#pragma once +#include "dap_test.h" +#include "dap_common.h" +void dap_network_monitor_test_run(void); diff --git a/test/libdap-test b/test/libdap-test index 7b40a4c..6d69cc4 160000 --- a/test/libdap-test +++ b/test/libdap-test @@ -1 +1 @@ -Subproject commit 7b40a4cdfae222f84ccedb11ecae471d01f5e33b +Subproject commit 6d69cc4be2ccbc5fc978adadab4fbcc52bc31d36 -- GitLab