#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; struct session_iterm { int session_id; // key struct health_check policy; // value1: deep copy int is_active; // value2 uint8_t mac[HC_MAC_LEN]; // value3 UT_hash_handle hh1; /* handle for first hash table */ }; 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 int health_check_session_foreach(); 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: 循环获取? 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 // return -1 : key exist // struct health_check *policy : need deep copy int health_check_session_add(int session_id, const struct health_check *policy) { 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; } // return 0 : success // return -1 : key not exist int health_check_session_del(int session_id) { 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; } // return 1 : active // return 0 : inactive // return -1 : key not exist int health_check_session_get_status(int session_id) { int status = 0; 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); if (!tmp) { LOG_DEBUG("health check session table get status: key %d not exists", session_id); pthread_rwlock_unlock(&g_handle.rwlock); return -1; } status = tmp->is_active; pthread_rwlock_unlock(&g_handle.rwlock); 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) { 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); } static 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) { uint8_t *p = NULL; 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); if (!tmp) { LOG_DEBUG("health check session table get status: key %d not exists", session_id); pthread_rwlock_unlock(&g_handle.rwlock); 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]); pthread_rwlock_unlock(&g_handle.rwlock); LOG_DEBUG("health check session get mac: %s", mac_buff); return 0; }