From 7ae1a79e8a534dcbc76aa21fc5688c371d0b2f54 Mon Sep 17 00:00:00 2001 From: wangmenglan Date: Tue, 21 Feb 2023 21:24:15 +0800 Subject: [PATCH] =?UTF-8?q?=E6=94=AF=E6=8C=81bfd?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- common/include/bfd.h | 17 +- common/src/bfd.cpp | 300 ++++++++++++++++++++++- common/test/CMakeLists.txt | 11 +- common/test/gtest_health_check_table.cpp | 219 +++++++++++++++++ conf/sce.conf | 8 +- platform/CMakeLists.txt | 1 + platform/include/health_check.h | 2 +- platform/src/health_check.cpp | 252 +++++++++++++++++-- platform/test/test_resource/sce.conf | 8 +- 9 files changed, 795 insertions(+), 23 deletions(-) create mode 100644 common/test/gtest_health_check_table.cpp diff --git a/common/include/bfd.h b/common/include/bfd.h index b660b29..61c232d 100644 --- a/common/include/bfd.h +++ b/common/include/bfd.h @@ -6,7 +6,22 @@ extern "C" { #endif - // TODO +#define BFD_PATHLEN 108 + +struct bfd_vtysh_client { + int fd; + char path[BFD_PATHLEN]; + + // exec after connect server + int (*pre_config)(struct bfd_vtysh_client *); +}; + +int bfd_vtysh_connect(struct bfd_vtysh_client *client); +void bfd_vtysh_close(struct bfd_vtysh_client *client); +int bfd_vtysh_add_dev(struct bfd_vtysh_client *client, const char *local_addr, const char *peer_addr, int retires, int interval_ms); +int bfd_vtysh_del_dev(struct bfd_vtysh_client *client, const char *local_addr, char *peer_addr); +int bfd_vtysh_get_dev_active(struct bfd_vtysh_client *client, char *addr); +int bfd_vtysh_pre_config(struct bfd_vtysh_client *client); #ifdef __cpluscplus } diff --git a/common/src/bfd.cpp b/common/src/bfd.cpp index b52cc1f..89dc526 100644 --- a/common/src/bfd.cpp +++ b/common/src/bfd.cpp @@ -1,3 +1,301 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "log.h" #include "bfd.h" -// TODO \ No newline at end of file +// return 0 : success +// return -1 : fail +int bfd_vtysh_connect(struct bfd_vtysh_client *client) +{ + int ret; + int sock, len; + struct stat s_stat; + struct sockaddr_un addr; + + /* Stat socket to see if we have permission to access it. */ + ret = stat(client->path, &s_stat); + if (ret < 0 && errno != ENOENT) { + LOG_DEBUG("bfd vtysh_connect(%s): stat = %s", client->path, strerror(errno)); + return -1; + } + + if (ret >= 0) { + if (!S_ISSOCK(s_stat.st_mode)) { + LOG_DEBUG("bfd vtysh_connect(%s): Not a socket", client->path); + return -1; + } + } + + sock = socket(AF_UNIX, SOCK_STREAM, 0); + if (sock < 0) { + LOG_DEBUG("bfd vtysh_connect(%s): socket = %s", client->path, strerror(errno)); + return -1; + } + + memset(&addr, 0, sizeof(addr)); + addr.sun_family = AF_UNIX; + strncpy(addr.sun_path, client->path, sizeof(addr.sun_path)); + len = sizeof(addr.sun_family) + strlen(addr.sun_path); + + ret = connect(sock, (struct sockaddr *)&addr, len); + if (ret < 0) { + LOG_DEBUG("bfd vtysh_connect(%s): connect = %s", client->path, strerror(errno)); + close(sock); + return -1; + } + + client->fd = sock; + + if (client->pre_config) { + ret = client->pre_config(client); + if (ret < 0) + bfd_vtysh_close(client); + } + + LOG_DEBUG("bfd vtysh_connect(%d): succ", client->fd); + return 0; +} + +// return 0 : success +// return -1 : fail +void bfd_vtysh_close(struct bfd_vtysh_client *client) +{ + if (client->fd >= 0) { + close(client->fd); + client->fd = -1; + } +} + +static int bfd_vtysh_client_send(struct bfd_vtysh_client *client, const char *line) +{ + int ret = 0; + ret = write(client->fd, line, strlen(line) + 1); + if (ret <= 0) { + /* close connection and try to reconnect */ + bfd_vtysh_close(client); + ret = bfd_vtysh_connect(client); + if (ret < 0) + return -1; + /* retry line */ + ret = write(client->fd, line, strlen(line) + 1); + if (ret <= 0) + return -1; + } + return 0; +} + +static ssize_t bfd_vtysh_client_receive(struct bfd_vtysh_client *client, char *buf, size_t bufsz) +{ + struct iovec iov[1] = { + { + .iov_base = buf, + .iov_len = bufsz, + }, + }; + union { + uint8_t buf[CMSG_SPACE(sizeof(int))]; + struct cmsghdr align; + } u; + struct msghdr mh = { + .msg_iov = iov, + .msg_iovlen = 1, + .msg_control = u.buf, + .msg_controllen = sizeof(u.buf), + }; + struct cmsghdr *cmh = CMSG_FIRSTHDR(&mh); + ssize_t ret; + + cmh->cmsg_level = SOL_SOCKET; + cmh->cmsg_type = SCM_RIGHTS; + cmh->cmsg_len = CMSG_LEN(sizeof(int)); + memset(CMSG_DATA(cmh), -1, sizeof(int)); + + do { + ret = recvmsg(client->fd, &mh, 0); + if (ret >= 0 || (errno != EINTR && errno != EAGAIN)) + break; + } while (1); + + if (cmh->cmsg_len == CMSG_LEN(sizeof(int))) { + int fd; + + memcpy(&fd, CMSG_DATA(cmh), sizeof(int)); + if (fd != -1) + close(fd); + } + return ret; +} + +static int bfd_vtysh_cmd_exec(struct bfd_vtysh_client *client, const char *cmd, char *data, size_t datasz) +{ + int ret = 0; + char *bufvalid; + char *buf = data; + + ret = bfd_vtysh_client_send(client, cmd); + if (ret < 0) + goto out_err; + + // if (data == NULL) + // return 0; + + bufvalid = buf; + do { + ssize_t nread; + nread = bfd_vtysh_client_receive(client, bufvalid, datasz-1); + if (nread < 0 && (errno == EINTR || errno == EAGAIN)) + continue; + + if (nread <= 0) { + LOG_DEBUG("bfd vtysh_receive: error reading from [%s] (%d)", strerror(errno), errno); + goto out_err; + } + + break; + + // LOG_DEBUG("bfd cmd [%s] nread[%d]", cmd, nread); + // bufvalid += nread; + // bufvalid[0] = '\0'; + // if (bufvalid - buf >= 4) + // end = (char*)memmem(bufvalid - 4, 4, "\0", 1); + // size_t textlen = (end ? end : bufvalid) - buf; + + // LOG_DEBUG("bfd cmd [%s] buf[%s]", cmd, buf); + // memmove(buf, buf + textlen, bufvalid - buf - textlen); + // bufvalid -= textlen; + // if (end) + // end -= textlen; + + // assert(((buf == bufvalid) + // || (bufvalid - buf <= 4 && buf[0] == 0x00))); + + // if (end && bufvalid - buf == 4) { + // if (memcmp(buf, terminator, 3)) + // goto out_err; + // ret = buf[3]; + // break; + // } + } while (1); + return 0; +out_err: + bfd_vtysh_close(client); + return -1; +} + +// return 0 : success +// return -1 : fail +int bfd_vtysh_pre_config(struct bfd_vtysh_client *client) +{ + int ret = 0; + char stackbuf[4096]; + + ret = bfd_vtysh_cmd_exec(client, "enable", stackbuf, sizeof(stackbuf)-1); + if (ret < 0) { + LOG_DEBUG("bfd vtysh cmd [enable] error"); + return -1; + } + ret = bfd_vtysh_cmd_exec(client, "conf t", stackbuf, sizeof(stackbuf)-1); + if (ret < 0) { + LOG_DEBUG("bfd vtysh cmd [conf t] error"); + return -1; + } + ret = bfd_vtysh_cmd_exec(client, "bfd", stackbuf, sizeof(stackbuf)-1); + if (ret < 0) { + LOG_DEBUG("bfd vtysh cmd [bfd] error"); + return -1; + } + return 0; +} + +// return 0 : success +// return -1 : fail +int bfd_vtysh_add_dev(struct bfd_vtysh_client *client, const char *local_addr, const char *peer_addr, int retires, int interval_ms) +{ + int ret = 0; + char cmd[256] = {0}; + char stackbuf[4096]; + + snprintf(cmd, sizeof(cmd), "peer %s local-address %s", peer_addr, local_addr); + ret = bfd_vtysh_cmd_exec(client, cmd, stackbuf, sizeof(stackbuf)-1); + if (ret < 0) { + LOG_DEBUG("bfd vtysh cmd [%s] error", cmd); + return -1; + } + + snprintf(cmd, sizeof(cmd), "detect-multiplier %d", retires); + ret = bfd_vtysh_cmd_exec(client, cmd, stackbuf, sizeof(stackbuf)-1); + if (ret < 0) { + LOG_DEBUG("bfd vtysh cmd [%s] error", cmd); + return -1; + } + + snprintf(cmd, sizeof(cmd), "receive-interval %d", interval_ms); + ret = bfd_vtysh_cmd_exec(client, cmd, stackbuf, sizeof(stackbuf)-1); + if (ret < 0) { + LOG_DEBUG("bfd vtysh cmd [%s] error", cmd); + return -1; + } + + snprintf(cmd, sizeof(cmd), "transmit-interval %d", interval_ms); + ret = bfd_vtysh_cmd_exec(client, cmd, stackbuf, sizeof(stackbuf)-1); + if (ret < 0) { + LOG_DEBUG("bfd vtysh cmd [%s] error", cmd); + return -1; + } + + return 0; +} + +// return 0 : success +// return -1 : fail +int bfd_vtysh_del_dev(struct bfd_vtysh_client *client, const char *local_addr, char *peer_addr) +{ + int ret = 0; + char cmd[256] = {0}; + char stackbuf[4096]; + + snprintf(cmd, sizeof(cmd), "no peer %s local-address %s", peer_addr, local_addr); + ret = bfd_vtysh_cmd_exec(client, cmd, stackbuf, sizeof(stackbuf)-1); + if (ret < 0) { + LOG_DEBUG("bfd vtysh cmd [%s] error", cmd); + return -1; + } + + return 0; +} + +// return 1 : up +// return 0 : down +// return -1 : fail +int bfd_vtysh_get_dev_active(struct bfd_vtysh_client *client, char *addr) +{ + int ret = 0; + char *p = NULL; + char cmd[256] = {0}; + char stackbuf[4096]; + + snprintf(cmd, sizeof(cmd), "do show bfd peer %s", addr); + ret = bfd_vtysh_cmd_exec(client, cmd, stackbuf, sizeof(stackbuf)-1); + if (ret < 0) { + LOG_DEBUG("bfd vtysh cmd [%s] error", cmd); + return -1; + } + + // ex: Status: up/Status: down + if ((p = strstr(stackbuf, "Status: ")) == NULL) + return -1; + if (strncmp(p+8, "up", 2) == 0) + return 1; + return 0; +} \ No newline at end of file diff --git a/common/test/CMakeLists.txt b/common/test/CMakeLists.txt index 7970b60..216d187 100644 --- a/common/test/CMakeLists.txt +++ b/common/test/CMakeLists.txt @@ -38,6 +38,14 @@ add_executable(gtest_utils gtest_utils.cpp) target_include_directories(gtest_utils PUBLIC ${CMAKE_SOURCE_DIR}/common/include) target_link_libraries(gtest_utils common gtest) +############################################################################### +# gtest_health_check_table +############################################################################### + +add_executable(gtest_health_check_table gtest_health_check_table.cpp) +target_include_directories(gtest_health_check_table PUBLIC ${CMAKE_SOURCE_DIR}/common/include ${CMAKE_SOURCE_DIR}/platform/include) +target_link_libraries(gtest_health_check_table common gtest platform) + ############################################################################### # gtest_discover_tests ############################################################################### @@ -47,4 +55,5 @@ gtest_discover_tests(gtest_addr_tuple4) gtest_discover_tests(gtest_session_table) gtest_discover_tests(gtest_raw_packet) gtest_discover_tests(gtest_ctrl_packet) -gtest_discover_tests(gtest_utils) \ No newline at end of file +gtest_discover_tests(gtest_utils) +gtest_discover_tests(gtest_health_check_table) diff --git a/common/test/gtest_health_check_table.cpp b/common/test/gtest_health_check_table.cpp new file mode 100644 index 0000000..b779c85 --- /dev/null +++ b/common/test/gtest_health_check_table.cpp @@ -0,0 +1,219 @@ +#include +#include + +#include "bfd.h" +#include "policy.h" +#include "health_check.h" + + +TEST(HEALTH_CHECK_TABLE, INSERT) +{ + // TEST Create + health_check_session_init("./sce.conf"); + + // TEST Insert + struct health_check policy1; + memset(&policy1, 0, sizeof(policy1)); + policy1.method = HEALTH_CHECK_METHOD_BFD; + snprintf(policy1.address, sizeof(policy1.address), "1.1.1.1"); + policy1.port = 65535; + policy1.retires = 11; + policy1.interval_ms = 111; + EXPECT_TRUE(health_check_session_add(1, &policy1) == 0); + EXPECT_TRUE(health_check_session_add(1, &policy1) == -1); + + struct health_check policy2; + memset(&policy2, 0, sizeof(policy2)); + policy2.method = HEALTH_CHECK_METHOD_BFD; + snprintf(policy2.address, sizeof(policy2.address), "2.2.2.2"); + policy2.port = 65535; + policy2.retires = 22; + policy2.interval_ms = 222; + EXPECT_TRUE(health_check_session_add(2, &policy2) == 0); + EXPECT_TRUE(health_check_session_add(2, &policy2) == -1); + + struct health_check policy3; + memset(&policy3, 0, sizeof(policy3)); + policy3.method = HEALTH_CHECK_METHOD_BFD; + snprintf(policy3.address, sizeof(policy3.address), "2001:0db8:0000:0000:0000:8a2e:0370:7334"); + policy3.port = 65535; + policy3.retires = 33; + policy3.interval_ms = 333; + EXPECT_TRUE(health_check_session_add(3, &policy3) == 0); + EXPECT_TRUE(health_check_session_add(3, &policy3) == -1); + + // TEST Delete By Session ID + EXPECT_TRUE(health_check_session_del(1) == 0); + EXPECT_TRUE(health_check_session_del(2) == 0); + EXPECT_TRUE(health_check_session_del(3) == 0); +} + +TEST(HEALTH_CHECK_TABLE, GET_STATUS) +{ + // TEST Insert + struct health_check policy1; + memset(&policy1, 0, sizeof(policy1)); + policy1.method = HEALTH_CHECK_METHOD_BFD; + snprintf(policy1.address, sizeof(policy1.address), "4.4.4.4"); + policy1.port = 65535; + policy1.retires = 5; + policy1.interval_ms = 300; + EXPECT_TRUE(health_check_session_add(4, &policy1) == 0); + + struct health_check policy2; + memset(&policy2, 0, sizeof(policy2)); + policy2.method = HEALTH_CHECK_METHOD_BFD; + snprintf(policy2.address, sizeof(policy2.address), "5.5.5.5"); + policy2.port = 65535; + policy2.retires = 5; + policy2.interval_ms = 300; + EXPECT_TRUE(health_check_session_add(5, &policy2) == 0); + + struct health_check policy3; + memset(&policy3, 0, sizeof(policy3)); + policy3.method = HEALTH_CHECK_METHOD_BFD; + snprintf(policy3.address, sizeof(policy3.address), "6.6.6.6"); + policy3.port = 65535; + policy3.retires = 5; + policy3.interval_ms = 300; + EXPECT_TRUE(health_check_session_add(6, &policy3) == 0); + + // TEST get status + EXPECT_TRUE(health_check_session_get_status(4) == 0); + EXPECT_TRUE(health_check_session_get_status(5) == 0); + EXPECT_TRUE(health_check_session_get_status(6) == 0); + EXPECT_TRUE(health_check_session_get_status(7) == -1); + + // TEST Delete By Session ID + EXPECT_TRUE(health_check_session_del(4) == 0); + EXPECT_TRUE(health_check_session_del(5) == 0); + EXPECT_TRUE(health_check_session_del(6) == 0); +} + +TEST(HEALTH_CHECK_TABLE, SET_STATUS) +{ + // TEST Insert + struct health_check policy1; + memset(&policy1, 0, sizeof(policy1)); + policy1.method = HEALTH_CHECK_METHOD_BFD; + snprintf(policy1.address, sizeof(policy1.address), "7.7.7.7"); + policy1.port = 65535; + policy1.retires = 5; + policy1.interval_ms = 300; + EXPECT_TRUE(health_check_session_add(7, &policy1) == 0); + + struct health_check policy2; + memset(&policy2, 0, sizeof(policy2)); + policy2.method = HEALTH_CHECK_METHOD_BFD; + snprintf(policy2.address, sizeof(policy2.address), "8.8.8.8"); + policy2.port = 65535; + policy2.retires = 5; + policy2.interval_ms = 300; + EXPECT_TRUE(health_check_session_add(8, &policy2) == 0); + + struct health_check policy3; + memset(&policy3, 0, sizeof(policy3)); + policy3.method = HEALTH_CHECK_METHOD_BFD; + snprintf(policy3.address, sizeof(policy3.address), "9.9.9.9"); + policy3.port = 65535; + policy3.retires = 5; + policy3.interval_ms = 300; + EXPECT_TRUE(health_check_session_add(9, &policy3) == 0); + + // TEST get status + EXPECT_TRUE(health_check_session_get_status(7) == 0); + EXPECT_TRUE(health_check_session_get_status(8) == 0); + EXPECT_TRUE(health_check_session_get_status(9) == 0); + + // TEST set status + EXPECT_TRUE(health_check_session_set_status(7, 1) == 0); + EXPECT_TRUE(health_check_session_set_status(8, 1) == 0); + EXPECT_TRUE(health_check_session_set_status(9, 1) == 0); + EXPECT_TRUE(health_check_session_set_status(10, 1) == -1); + + // TEST get status + EXPECT_TRUE(health_check_session_get_status(7) == 1); + EXPECT_TRUE(health_check_session_get_status(8) == 1); + EXPECT_TRUE(health_check_session_get_status(9) == 1); + + // TEST Delete By Session ID + EXPECT_TRUE(health_check_session_del(7) == 0); + EXPECT_TRUE(health_check_session_del(8) == 0); + EXPECT_TRUE(health_check_session_del(9) == 0); +} + +TEST(HEALTH_CHECK_TABLE, DELETE) +{ + // TEST Insert + struct health_check policy1; + memset(&policy1, 0, sizeof(policy1)); + policy1.method = HEALTH_CHECK_METHOD_BFD; + snprintf(policy1.address, sizeof(policy1.address), "10.10.10.10"); + policy1.port = 65535; + policy1.retires = 5; + policy1.interval_ms = 300; + EXPECT_TRUE(health_check_session_add(10, &policy1) == 0); + + struct health_check policy2; + memset(&policy2, 0, sizeof(policy2)); + policy2.method = HEALTH_CHECK_METHOD_BFD; + snprintf(policy2.address, sizeof(policy2.address), "11.11.11.11"); + policy2.port = 65535; + policy2.retires = 5; + policy2.interval_ms = 300; + EXPECT_TRUE(health_check_session_add(11, &policy2) == 0); + + struct health_check policy3; + memset(&policy3, 0, sizeof(policy3)); + policy3.method = HEALTH_CHECK_METHOD_BFD; + snprintf(policy3.address, sizeof(policy3.address), "12.12.12.12"); + policy3.port = 65535; + policy3.retires = 5; + policy3.interval_ms = 300; + EXPECT_TRUE(health_check_session_add(12, &policy3) == 0); + + // TEST Delete By Session ID + EXPECT_TRUE(health_check_session_del(10) == 0); + EXPECT_TRUE(health_check_session_del(10) == -1); + EXPECT_TRUE(health_check_session_del(11) == 0); + EXPECT_TRUE(health_check_session_del(11) == -1); + EXPECT_TRUE(health_check_session_del(12) == 0); + EXPECT_TRUE(health_check_session_del(12) == -1); +} + +static void *bfd_get_status(void *arg) +{ + while(1) { + health_check_session_get_status(10); + sleep(1); + } + return NULL; +} + +TEST(HEALTH_CHECK_TABLE, CHECK_BFD_ENVIRONMENT) +{ + // TEST Insert + struct health_check policy1; + memset(&policy1, 0, sizeof(policy1)); + policy1.method = HEALTH_CHECK_METHOD_BFD; + snprintf(policy1.address, sizeof(policy1.address), "192.168.32.82"); + policy1.port = 65535; + policy1.retires = 5; + policy1.interval_ms = 300; + EXPECT_TRUE(health_check_session_add(10, &policy1) == 0); + + pthread_t pid1; + pthread_create(&pid1, NULL, bfd_get_status, NULL); + + char mac[18] = {0}; + while(1) { + health_check_session_get_mac(10, mac); + sleep(5); + } +} + +int main(int argc, char **argv) +{ + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} \ No newline at end of file diff --git a/conf/sce.conf b/conf/sce.conf index 90c4f7c..8656d0f 100644 --- a/conf/sce.conf +++ b/conf/sce.conf @@ -44,4 +44,10 @@ statsd_port=8100 statsd_format=1 statsd_cycle=2 prometheus_listen_port=9001 -prometheus_listen_url=/sce_prometheus \ No newline at end of file +prometheus_listen_url=/sce_prometheus + +[bfdd] +path=/var/run/frr/bfdd.vty +device=eth0 +local_address=127.0.0.1 +gateway=127.0.0.1 diff --git a/platform/CMakeLists.txt b/platform/CMakeLists.txt index a8f52e9..496ca38 100644 --- a/platform/CMakeLists.txt +++ b/platform/CMakeLists.txt @@ -6,6 +6,7 @@ target_link_libraries(platform PUBLIC MESA_field_stat) target_link_libraries(platform PUBLIC maatframe) target_link_libraries(platform PUBLIC mrzcpd) target_link_libraries(platform PUBLIC cjson) +target_link_libraries(platform PUBLIC pthread) target_include_directories(platform PUBLIC ${CMAKE_CURRENT_LIST_DIR}/include/) add_executable(sce src/main.cpp) diff --git a/platform/include/health_check.h b/platform/include/health_check.h index 3a5c70c..d8c617e 100644 --- a/platform/include/health_check.h +++ b/platform/include/health_check.h @@ -8,7 +8,7 @@ extern "C" #include "policy.h" -void health_check_session_init(); +void health_check_session_init(const char *profile); // return 0 : success // return -1 : key exist diff --git a/platform/src/health_check.cpp b/platform/src/health_check.cpp index 6aac6f7..db906bf 100644 --- a/platform/src/health_check.cpp +++ b/platform/src/health_check.cpp @@ -1,11 +1,34 @@ #include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "log.h" +#include "uthash.h" +#include "bfd.h" #include "health_check.h" + +#define HC_MAC_LEN 6 +#define HC_DEV_NAME_LEN 16 +#define HC_LOCAL_ADDRESS_LEN 64 + struct session_table { // rwlock ???; // handler; + struct session_iterm *root_by_id; + pthread_rwlock_t rwlock; }; static struct session_table g_handle; @@ -15,16 +38,49 @@ struct session_iterm int session_id; // key struct health_check policy; // value1: deep copy - int is_active; // value2 - char dst_ip[16]; // value3 - char dst_mac[32]; // value4 + int is_active; // value2 + uint8_t mac[HC_MAC_LEN]; // value3 + + UT_hash_handle hh1; /* handle for first hash table */ }; -void health_check_session_init() +int sleep_ms = 300; +char path[BFD_PATHLEN]; +char hc_dev_name[HC_DEV_NAME_LEN]; +char local_address[HC_LOCAL_ADDRESS_LEN]; +char gateway_address[HC_LOCAL_ADDRESS_LEN]; +uint8_t default_gw_mac[HC_MAC_LEN]; + +static int get_mac_by_addr(char *addr, uint8_t *buf); + +static struct session_iterm *health_check_get_iterm_by_id(int session_id) +{ + struct session_iterm *tmp = NULL; + pthread_rwlock_rdlock(&g_handle.rwlock); + HASH_FIND(hh1, g_handle.root_by_id, &session_id, sizeof(session_id), tmp); + pthread_rwlock_unlock(&g_handle.rwlock); + return tmp; +} + +void health_check_session_init(const char *profile) { memset(&g_handle, 0, sizeof(g_handle)); + pthread_rwlock_init(&g_handle.rwlock, NULL); + MESA_load_profile_string_def(profile, "bfdd", "path", path, sizeof(path), "/var/run/frr/bfdd.vty"); + MESA_load_profile_string_def(profile, "bfdd", "device", hc_dev_name, sizeof(hc_dev_name), "eth0"); + MESA_load_profile_string_def(profile, "bfdd", "local_address", local_address, sizeof(local_address), "127.0.0.1"); + MESA_load_profile_string_def(profile, "bfdd", "gateway", gateway_address, sizeof(gateway_address), "127.0.0.1"); - // TODO + // TODO: 循环获取? + get_mac_by_addr(gateway_address, default_gw_mac); + health_check_session_foreach(); +} + +static void health_check_session_init_bfd_client(struct bfd_vtysh_client *client) +{ + memset(client, 0, sizeof(*client)); + snprintf(client->path, sizeof(client->path), path); + client->pre_config = bfd_vtysh_pre_config; } // return 0 : success @@ -32,7 +88,38 @@ void health_check_session_init() // struct health_check *policy : need deep copy int health_check_session_add(int session_id, const struct health_check *policy) { - // TODO + int ret = 0; + struct bfd_vtysh_client client; + struct session_iterm *tmp = NULL; + + tmp = health_check_get_iterm_by_id(session_id); + if (tmp) { + LOG_DEBUG("health check session table insert: key %d exists", session_id); + return -1; + } + + tmp = (struct session_iterm *)calloc(1, sizeof(struct session_iterm)); + assert(tmp); + + health_check_session_init_bfd_client(&client); + bfd_vtysh_connect(&client); + ret = bfd_vtysh_add_dev(&client, local_address, policy->address, policy->retires, policy->interval_ms); + if (ret != 0) { + LOG_DEBUG("bfd vtysh add dev address [%s] failed!", tmp->policy.address); + bfd_vtysh_close(&client); + return -1; + } + bfd_vtysh_close(&client); + + memset(tmp, 0, sizeof(*tmp)); + tmp->session_id = session_id; + memcpy(&tmp->policy, policy, sizeof(struct health_check)); + + pthread_rwlock_wrlock(&g_handle.rwlock); + HASH_ADD(hh1, g_handle.root_by_id, session_id, sizeof(tmp->session_id), tmp); + pthread_rwlock_unlock(&g_handle.rwlock); + + LOG_DEBUG("health check session table insert: key %d success", session_id); return 0; } @@ -40,7 +127,33 @@ int health_check_session_add(int session_id, const struct health_check *policy) // return -1 : key not exist int health_check_session_del(int session_id) { - // TODO + int ret = 0; + struct bfd_vtysh_client client; + struct session_iterm *tmp = NULL; + + tmp = health_check_get_iterm_by_id(session_id); + if (!tmp) { + LOG_DEBUG("health check session table delete: key %d not exists", session_id); + return -1; + } + + health_check_session_init_bfd_client(&client); + bfd_vtysh_connect(&client); + ret = bfd_vtysh_del_dev(&client, local_address, tmp->policy.address); + if (ret != 0) { + LOG_DEBUG("bfd vtysh delete dev address [%s] failed!", tmp->policy.address); + bfd_vtysh_close(&client); + return -1; + } + bfd_vtysh_close(&client); + + pthread_rwlock_wrlock(&g_handle.rwlock); + HASH_DELETE(hh1, g_handle.root_by_id, tmp); + pthread_rwlock_unlock(&g_handle.rwlock); + free(tmp); + tmp = NULL; + + LOG_DEBUG("health check session table delete: key %d success", session_id); return 0; } @@ -49,28 +162,133 @@ int health_check_session_del(int session_id) // return -1 : key not exist int health_check_session_get_status(int session_id) { - // TODO - return 1; + int status = 0; + struct session_iterm *tmp = NULL; + + tmp = health_check_get_iterm_by_id(session_id); + if (!tmp) { + LOG_DEBUG("health check session table get status: key %d not exists", session_id); + return -1; + } + + status = tmp->is_active; + LOG_DEBUG("health check session get status: %d", status); + return status; } // return 0 : success // return -1 : key not exist int health_check_session_set_status(int session_id, int is_active) { - // TODO + struct session_iterm *tmp = NULL; + + pthread_rwlock_wrlock(&g_handle.rwlock); + HASH_FIND(hh1, g_handle.root_by_id, &session_id, sizeof(session_id), tmp); + if (!tmp) { + LOG_DEBUG("health check session table get status: key %d not exists", session_id); + pthread_rwlock_unlock(&g_handle.rwlock); + return -1; + } + + tmp->is_active = is_active; + pthread_rwlock_unlock(&g_handle.rwlock); return 0; } +static int get_mac_by_addr(char *addr, uint8_t *buf) +{ + int sfd, saved_errno, ret; + struct arpreq arp_req; + struct sockaddr_in *sin; + + sin = (struct sockaddr_in *)&(arp_req.arp_pa); + + memset(&arp_req, 0, sizeof(arp_req)); + sin->sin_family = AF_INET; + inet_pton(AF_INET, addr, &(sin->sin_addr)); + snprintf(arp_req.arp_dev, IFNAMSIZ, hc_dev_name); + + sfd = socket(AF_INET, SOCK_DGRAM, 0); + + saved_errno = errno; + ret = ioctl(sfd, SIOCGARP, &arp_req); + if (ret < 0) { + LOG_DEBUG("Get ARP entry failed : %s\n", strerror(errno)); + return -1; + } + errno = saved_errno; + + if (arp_req.arp_flags & ATF_COM) + memcpy(buf, arp_req.arp_ha.sa_data, HC_MAC_LEN); + else + memcpy(buf, default_gw_mac, HC_MAC_LEN); + + LOG_DEBUG("MAC: %02x:%02x:%02x:%02x:%02x:%02x\n", + buf[0], buf[1], buf[2], buf[3], buf[4], buf[5]); + return 0; +} + +static void *_health_check_session_foreach(void *arg) +{ + int is_active = 0; + struct bfd_vtysh_client client; + struct session_iterm *tmp = NULL; + struct session_iterm *node = NULL; + + health_check_session_init_bfd_client(&client); + bfd_vtysh_connect(&client); + + while(1) { + // TODO: 改为读锁,更新数据入队列,通过写锁更新 + pthread_rwlock_wrlock(&g_handle.rwlock); + HASH_ITER(hh1, g_handle.root_by_id, node, tmp) { + if (node->policy.method != HEALTH_CHECK_METHOD_BFD) + continue; + + is_active = bfd_vtysh_get_dev_active(&client, node->policy.address); + if (is_active == -1) + continue; + if (node->is_active != is_active) { + node->is_active = is_active; + if (node->is_active == 1) { + get_mac_by_addr(node->policy.address, node->mac); + } + else { + memset(node->mac, 0, sizeof(node->mac)); + } + } + if (sleep_ms > node->policy.interval_ms) + sleep_ms = node->policy.interval_ms; + } + pthread_rwlock_unlock(&g_handle.rwlock); + usleep(sleep_ms*1000); + } + bfd_vtysh_close(&client); +} + +int health_check_session_foreach() +{ + pthread_t pid; + pthread_create(&pid, NULL, _health_check_session_foreach, NULL); + pthread_detach(pid); + return pid; +} + // return 0 : success // return -1 : key not exist int health_check_session_get_mac(int session_id, char *mac_buff) { - strcpy(mac_buff, "66:AA:AA:AA:AA:AA"); - // TODO - return 0; -} + uint8_t *p = NULL; + struct session_iterm *tmp = NULL; -void health_check_session_foreach() -{ - // TODO + tmp = health_check_get_iterm_by_id(session_id); + if (!tmp) { + LOG_DEBUG("health check session table get status: key %d not exists", session_id); + return -1; + } + + p = (uint8_t *)tmp->mac; + snprintf(mac_buff, 18, "%02x:%02x:%02x:%02x:%02x:%02x", p[0], p[1], p[2], p[3], p[4], p[5]); + LOG_DEBUG("health check session get mac: %s", mac_buff); + return 0; } \ No newline at end of file diff --git a/platform/test/test_resource/sce.conf b/platform/test/test_resource/sce.conf index 607a4c1..50b700a 100644 --- a/platform/test/test_resource/sce.conf +++ b/platform/test/test_resource/sce.conf @@ -18,4 +18,10 @@ json_cfg_file=test_resource/sce.json foreign_cont_dir=test_resource/foreign_files redis_db_idx=0 redis_server=127.0.0.1 -redis_port_range=6379 \ No newline at end of file +redis_port_range=6379 + +[bfdd] +path=/var/run/frr/bfdd.vty +device=eth0 +local_address=127.0.0.1 +gateway=127.0.0.1 \ No newline at end of file