#include #include #include #include #include #include #include #include #include #include #include #include #include "log.h" #include "bfd.h" // 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; }