301 lines
7.4 KiB
C++
301 lines
7.4 KiB
C++
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <sys/un.h>
|
|
#include <sys/stat.h>
|
|
#include <errno.h>
|
|
#include <sys/types.h>
|
|
#include <sys/socket.h>
|
|
#include <sys/uio.h>
|
|
#include <inttypes.h>
|
|
#include <stdlib.h>
|
|
#include <unistd.h>
|
|
#include <assert.h>
|
|
|
|
#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;
|
|
} |