summaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/base64.c193
-rw-r--r--lib/base64.h45
-rw-r--r--lib/checksum.c85
-rw-r--r--lib/checksum.h43
-rw-r--r--lib/subdir.am4
-rw-r--r--lib/vty.c171
-rw-r--r--lib/vty.h26
-rw-r--r--lib/yang_wrappers.c59
-rw-r--r--lib/yang_wrappers.h7
-rw-r--r--lib/zlog.c5
-rw-r--r--lib/zlog.h3
-rw-r--r--lib/zlog_live.c245
-rw-r--r--lib/zlog_live.h53
13 files changed, 867 insertions, 72 deletions
diff --git a/lib/base64.c b/lib/base64.c
new file mode 100644
index 000000000..e3f238969
--- /dev/null
+++ b/lib/base64.c
@@ -0,0 +1,193 @@
+/*
+ * This is part of the libb64 project, and has been placed in the public domain.
+ * For details, see http://sourceforge.net/projects/libb64
+ */
+
+#include "base64.h"
+
+static const int CHARS_PER_LINE = 72;
+static const char *ENCODING =
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+void base64_init_encodestate(struct base64_encodestate *state_in)
+{
+ state_in->step = step_A;
+ state_in->result = 0;
+ state_in->stepcount = 0;
+}
+
+char base64_encode_value(char value_in)
+{
+ if (value_in > 63)
+ return '=';
+ return ENCODING[(int)value_in];
+}
+
+int base64_encode_block(const char *plaintext_in, int length_in, char *code_out,
+ struct base64_encodestate *state_in)
+{
+ const char *plainchar = plaintext_in;
+ const char *const plaintextend = plaintext_in + length_in;
+ char *codechar = code_out;
+ char result;
+ char fragment;
+
+ result = state_in->result;
+
+ switch (state_in->step) {
+ while (1) {
+ case step_A:
+ if (plainchar == plaintextend) {
+ state_in->result = result;
+ state_in->step = step_A;
+ return codechar - code_out;
+ }
+ fragment = *plainchar++;
+ result = (fragment & 0x0fc) >> 2;
+ *codechar++ = base64_encode_value(result);
+ result = (fragment & 0x003) << 4;
+ /* fall through */
+ case step_B:
+ if (plainchar == plaintextend) {
+ state_in->result = result;
+ state_in->step = step_B;
+ return codechar - code_out;
+ }
+ fragment = *plainchar++;
+ result |= (fragment & 0x0f0) >> 4;
+ *codechar++ = base64_encode_value(result);
+ result = (fragment & 0x00f) << 2;
+ /* fall through */
+ case step_C:
+ if (plainchar == plaintextend) {
+ state_in->result = result;
+ state_in->step = step_C;
+ return codechar - code_out;
+ }
+ fragment = *plainchar++;
+ result |= (fragment & 0x0c0) >> 6;
+ *codechar++ = base64_encode_value(result);
+ result = (fragment & 0x03f) >> 0;
+ *codechar++ = base64_encode_value(result);
+
+ ++(state_in->stepcount);
+ if (state_in->stepcount == CHARS_PER_LINE/4) {
+ *codechar++ = '\n';
+ state_in->stepcount = 0;
+ }
+ }
+ }
+ /* control should not reach here */
+ return codechar - code_out;
+}
+
+int base64_encode_blockend(char *code_out, struct base64_encodestate *state_in)
+{
+ char *codechar = code_out;
+
+ switch (state_in->step) {
+ case step_B:
+ *codechar++ = base64_encode_value(state_in->result);
+ *codechar++ = '=';
+ *codechar++ = '=';
+ break;
+ case step_C:
+ *codechar++ = base64_encode_value(state_in->result);
+ *codechar++ = '=';
+ break;
+ case step_A:
+ break;
+ }
+ *codechar++ = '\n';
+
+ return codechar - code_out;
+}
+
+
+signed char base64_decode_value(signed char value_in)
+{
+ static const signed char decoding[] = {
+ 62, -1, -1, -1, 63, 52, 53, 54,
+ 55, 56, 57, 58, 59, 60, 61, -1,
+ -1, -1, -2, -1, -1, -1, 0, 1,
+ 2, 3, 4, 5, 6, 7, 8, 9,
+ 10, 11, 12, 13, 14, 15, 16, 17,
+ 18, 19, 20, 21, 22, 23, 24, 25,
+ -1, -1, -1, -1, -1, -1, 26, 27,
+ 28, 29, 30, 31, 32, 33, 34, 35,
+ 36, 37, 38, 39, 40, 41, 42, 43,
+ 44, 45, 46, 47, 48, 49, 50, 51
+ };
+ value_in -= 43;
+ if (value_in < 0 || value_in >= 80)
+ return -1;
+ return decoding[(int)value_in];
+}
+
+void base64_init_decodestate(struct base64_decodestate *state_in)
+{
+ state_in->step = step_a;
+ state_in->plainchar = 0;
+}
+
+int base64_decode_block(const char *code_in, int length_in, char *plaintext_out,
+ struct base64_decodestate *state_in)
+{
+ const char *codec = code_in;
+ char *plainc = plaintext_out;
+ signed char fragmt;
+
+ *plainc = state_in->plainchar;
+
+ switch (state_in->step) {
+ while (1) {
+ case step_a:
+ do {
+ if (codec == code_in+length_in) {
+ state_in->step = step_a;
+ state_in->plainchar = *plainc;
+ return plainc - plaintext_out;
+ }
+ fragmt = base64_decode_value(*codec++);
+ } while (fragmt < 0);
+ *plainc = (fragmt & 0x03f) << 2;
+ /* fall through */
+ case step_b:
+ do {
+ if (codec == code_in+length_in) {
+ state_in->step = step_b;
+ state_in->plainchar = *plainc;
+ return plainc - plaintext_out;
+ }
+ fragmt = base64_decode_value(*codec++);
+ } while (fragmt < 0);
+ *plainc++ |= (fragmt & 0x030) >> 4;
+ *plainc = (fragmt & 0x00f) << 4;
+ /* fall through */
+ case step_c:
+ do {
+ if (codec == code_in+length_in) {
+ state_in->step = step_c;
+ state_in->plainchar = *plainc;
+ return plainc - plaintext_out;
+ }
+ fragmt = base64_decode_value(*codec++);
+ } while (fragmt < 0);
+ *plainc++ |= (fragmt & 0x03c) >> 2;
+ *plainc = (fragmt & 0x003) << 6;
+ /* fall through */
+ case step_d:
+ do {
+ if (codec == code_in+length_in) {
+ state_in->step = step_d;
+ state_in->plainchar = *plainc;
+ return plainc - plaintext_out;
+ }
+ fragmt = base64_decode_value(*codec++);
+ } while (fragmt < 0);
+ *plainc++ |= (fragmt & 0x03f);
+ }
+ }
+ /* control should not reach here */
+ return plainc - plaintext_out;
+}
diff --git a/lib/base64.h b/lib/base64.h
new file mode 100644
index 000000000..3dc1559aa
--- /dev/null
+++ b/lib/base64.h
@@ -0,0 +1,45 @@
+/*
+ * This is part of the libb64 project, and has been placed in the public domain.
+ * For details, see http://sourceforge.net/projects/libb64
+ */
+
+#ifndef _BASE64_H_
+#define _BASE64_H_
+
+enum base64_encodestep {
+ step_A, step_B, step_C
+};
+
+struct base64_encodestate {
+ enum base64_encodestep step;
+ char result;
+ int stepcount;
+};
+
+void base64_init_encodestate(struct base64_encodestate *state_in);
+
+char base64_encode_value(char value_in);
+
+int base64_encode_block(const char *plaintext_in, int length_in, char *code_out,
+ struct base64_encodestate *state_in);
+
+int base64_encode_blockend(char *code_out, struct base64_encodestate *state_in);
+
+
+enum base64_decodestep {
+ step_a, step_b, step_c, step_d
+};
+
+struct base64_decodestate {
+ enum base64_decodestep step;
+ char plainchar;
+};
+
+void base64_init_decodestate(struct base64_decodestate *state_in);
+
+signed char base64_decode_value(signed char value_in);
+
+int base64_decode_block(const char *code_in, int length_in, char *plaintext_out,
+ struct base64_decodestate *state_in);
+
+#endif /* _BASE64_H_ */
diff --git a/lib/checksum.c b/lib/checksum.c
index 347337004..6c5f06de4 100644
--- a/lib/checksum.c
+++ b/lib/checksum.c
@@ -9,13 +9,24 @@
#include <zebra.h>
#include "checksum.h"
-int /* return checksum in low-order 16 bits */
- in_cksum(void *parg, int nbytes)
+#define add_carry(dst, add) \
+ do { \
+ typeof(dst) _add = (add); \
+ dst += _add; \
+ if (dst < _add) \
+ dst++; \
+ } while (0)
+
+uint16_t in_cksumv(const struct iovec *iov, size_t iov_len)
{
- unsigned short *ptr = parg;
- register long sum; /* assumes long == 32 bits */
- unsigned short oddbyte;
- register unsigned short answer; /* assumes unsigned short == 16 bits */
+ const struct iovec *iov_end;
+ uint32_t sum = 0;
+
+ union {
+ uint8_t bytes[2];
+ uint16_t word;
+ } wordbuf;
+ bool have_oddbyte = false;
/*
* Our algorithm is simple, using a 32-bit accumulator (sum),
@@ -23,17 +34,42 @@ int /* return checksum in low-order 16 bits */
* all the carry bits from the top 16 bits into the lower 16 bits.
*/
- sum = 0;
- while (nbytes > 1) {
- sum += *ptr++;
- nbytes -= 2;
+ for (iov_end = iov + iov_len; iov < iov_end; iov++) {
+ const uint8_t *ptr, *end;
+
+ ptr = (const uint8_t *)iov->iov_base;
+ end = ptr + iov->iov_len;
+ if (ptr == end)
+ continue;
+
+ if (have_oddbyte) {
+ have_oddbyte = false;
+ wordbuf.bytes[1] = *ptr++;
+
+ add_carry(sum, wordbuf.word);
+ }
+
+ while (ptr + 8 <= end) {
+ add_carry(sum, *(const uint32_t *)(ptr + 0));
+ add_carry(sum, *(const uint32_t *)(ptr + 4));
+ ptr += 8;
+ }
+
+ while (ptr + 2 <= end) {
+ add_carry(sum, *(const uint16_t *)ptr);
+ ptr += 2;
+ }
+
+ if (ptr + 1 <= end) {
+ wordbuf.bytes[0] = *ptr++;
+ have_oddbyte = true;
+ }
}
/* mop up an odd byte, if necessary */
- if (nbytes == 1) {
- oddbyte = 0; /* make sure top half is zero */
- *((uint8_t *)&oddbyte) = *(uint8_t *)ptr; /* one byte only */
- sum += oddbyte;
+ if (have_oddbyte) {
+ wordbuf.bytes[1] = 0;
+ add_carry(sum, wordbuf.word);
}
/*
@@ -42,26 +78,7 @@ int /* return checksum in low-order 16 bits */
sum = (sum >> 16) + (sum & 0xffff); /* add high-16 to low-16 */
sum += (sum >> 16); /* add carry */
- answer = ~sum; /* ones-complement, then truncate to 16 bits */
- return (answer);
-}
-
-int in_cksum_with_ph4(struct ipv4_ph *ph, void *data, int nbytes)
-{
- uint8_t dat[sizeof(struct ipv4_ph) + nbytes];
-
- memcpy(dat, ph, sizeof(struct ipv4_ph));
- memcpy(dat + sizeof(struct ipv4_ph), data, nbytes);
- return in_cksum(dat, sizeof(dat));
-}
-
-int in_cksum_with_ph6(struct ipv6_ph *ph, void *data, int nbytes)
-{
- uint8_t dat[sizeof(struct ipv6_ph) + nbytes];
-
- memcpy(dat, ph, sizeof(struct ipv6_ph));
- memcpy(dat + sizeof(struct ipv6_ph), data, nbytes);
- return in_cksum(dat, sizeof(dat));
+ return ~sum;
}
/* Fletcher Checksum -- Refer to RFC1008. */
diff --git a/lib/checksum.h b/lib/checksum.h
index 56771d4f2..508c3f38a 100644
--- a/lib/checksum.h
+++ b/lib/checksum.h
@@ -1,3 +1,6 @@
+#ifndef _FRR_CHECKSUM_H
+#define _FRR_CHECKSUM_H
+
#include <stdint.h>
#include <netinet/in.h>
@@ -24,9 +27,41 @@ struct ipv6_ph {
uint8_t next_hdr;
} __attribute__((packed));
-extern int in_cksum(void *data, int nbytes);
-extern int in_cksum_with_ph4(struct ipv4_ph *ph, void *data, int nbytes);
-extern int in_cksum_with_ph6(struct ipv6_ph *ph, void *data, int nbytes);
+
+extern uint16_t in_cksumv(const struct iovec *iov, size_t iov_len);
+
+static inline uint16_t in_cksum(const void *data, size_t nbytes)
+{
+ struct iovec iov[1];
+
+ iov[0].iov_base = (void *)data;
+ iov[0].iov_len = nbytes;
+ return in_cksumv(iov, array_size(iov));
+}
+
+static inline uint16_t in_cksum_with_ph4(const struct ipv4_ph *ph,
+ const void *data, size_t nbytes)
+{
+ struct iovec iov[2];
+
+ iov[0].iov_base = (void *)ph;
+ iov[0].iov_len = sizeof(*ph);
+ iov[1].iov_base = (void *)data;
+ iov[1].iov_len = nbytes;
+ return in_cksumv(iov, array_size(iov));
+}
+
+static inline uint16_t in_cksum_with_ph6(const struct ipv6_ph *ph,
+ const void *data, size_t nbytes)
+{
+ struct iovec iov[2];
+
+ iov[0].iov_base = (void *)ph;
+ iov[0].iov_len = sizeof(*ph);
+ iov[1].iov_base = (void *)data;
+ iov[1].iov_len = nbytes;
+ return in_cksumv(iov, array_size(iov));
+}
#define FLETCHER_CHECKSUM_VALIDATE 0xffff
extern uint16_t fletcher_checksum(uint8_t *, const size_t len,
@@ -35,3 +70,5 @@ extern uint16_t fletcher_checksum(uint8_t *, const size_t len,
#ifdef __cplusplus
}
#endif
+
+#endif /* _FRR_CHECKSUM_H */
diff --git a/lib/subdir.am b/lib/subdir.am
index 648ab7f14..b505e235c 100644
--- a/lib/subdir.am
+++ b/lib/subdir.am
@@ -8,6 +8,7 @@ lib_libfrr_la_LIBADD = $(LIBCAP) $(UNWIND_LIBS) $(LIBYANG_LIBS) $(LUA_LIB) $(UST
lib_libfrr_la_SOURCES = \
lib/agg_table.c \
lib/atomlist.c \
+ lib/base64.c \
lib/bfd.c \
lib/buffer.c \
lib/checksum.c \
@@ -113,6 +114,7 @@ lib_libfrr_la_SOURCES = \
lib/zlog.c \
lib/zlog_5424.c \
lib/zlog_5424_cli.c \
+ lib/zlog_live.c \
lib/zlog_targets.c \
lib/printf/printf-pos.c \
lib/printf/vfprintf.c \
@@ -177,6 +179,7 @@ clippy_scan += \
pkginclude_HEADERS += \
lib/agg_table.h \
lib/atomlist.h \
+ lib/base64.h \
lib/bfd.h \
lib/bitfield.h \
lib/buffer.h \
@@ -287,6 +290,7 @@ pkginclude_HEADERS += \
lib/zebra.h \
lib/zlog.h \
lib/zlog_5424.h \
+ lib/zlog_live.h \
lib/zlog_targets.h \
lib/pbr.h \
lib/routing_nb.h \
diff --git a/lib/vty.c b/lib/vty.c
index a42b8c92b..6aa8a0bbb 100644
--- a/lib/vty.c
+++ b/lib/vty.c
@@ -1314,8 +1314,6 @@ static void vty_read(struct thread *thread)
vty_event(VTY_READ, vty);
return;
}
- vty->monitor = 0; /* disable monitoring to avoid
- infinite recursion */
flog_err(
EC_LIB_SOCKET,
"%s: read error on vty client fd %d, closing: %s",
@@ -1464,6 +1462,11 @@ static void vty_read(struct thread *thread)
vty_out(vty, "\n");
buffer_flush_available(vty->obuf, vty->wfd);
vty_execute(vty);
+
+ if (vty->pass_fd != -1) {
+ close(vty->pass_fd);
+ vty->pass_fd = -1;
+ }
break;
case '\t':
vty_complete_command(vty);
@@ -1524,8 +1527,6 @@ static void vty_flush(struct thread *thread)
vty->lines >= 0 ? vty->lines : vty->height, erase, 0);
switch (flushrc) {
case BUFFER_ERROR:
- vty->monitor =
- 0; /* disable monitoring to avoid infinite recursion */
zlog_info("buffer_flush failed on vty client fd %d/%d, closing",
vty->fd, vty->wfd);
buffer_reset(vty->lbuf);
@@ -1561,6 +1562,7 @@ struct vty *vty_new(void)
new->obuf = buffer_new(0); /* Use default buffer size. */
new->buf = XCALLOC(MTYPE_VTY, VTY_BUFSIZ);
new->max = VTY_BUFSIZ;
+ new->pass_fd = -1;
return new;
}
@@ -2010,15 +2012,68 @@ static void vtysh_accept(struct thread *thread)
vty_event(VTYSH_READ, vty);
}
+static int vtysh_do_pass_fd(struct vty *vty)
+{
+ struct iovec iov[1] = {
+ {
+ .iov_base = vty->pass_fd_status,
+ .iov_len = sizeof(vty->pass_fd_status),
+ },
+ };
+ union {
+ uint8_t buf[CMSG_SPACE(sizeof(int))];
+ struct cmsghdr align;
+ } u;
+ struct msghdr mh = {
+ .msg_iov = iov,
+ .msg_iovlen = array_size(iov),
+ .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));
+ memcpy(CMSG_DATA(cmh), &vty->pass_fd, sizeof(int));
+
+ ret = sendmsg(vty->wfd, &mh, 0);
+ if (ret < 0 && ERRNO_IO_RETRY(errno))
+ return BUFFER_PENDING;
+
+ close(vty->pass_fd);
+ vty->pass_fd = -1;
+ vty->status = VTY_NORMAL;
+
+ if (ret <= 0)
+ return BUFFER_ERROR;
+
+ /* resume accepting commands (suspended in vtysh_read) */
+ vty_event(VTYSH_READ, vty);
+
+ if ((size_t)ret < sizeof(vty->pass_fd_status)) {
+ size_t remains = sizeof(vty->pass_fd_status) - ret;
+
+ buffer_put(vty->obuf, vty->pass_fd_status + ret, remains);
+ return BUFFER_PENDING;
+ }
+ return BUFFER_EMPTY;
+}
+
static int vtysh_flush(struct vty *vty)
{
- switch (buffer_flush_available(vty->obuf, vty->wfd)) {
+ int ret;
+
+ ret = buffer_flush_available(vty->obuf, vty->wfd);
+ if (ret == BUFFER_EMPTY && vty->status == VTY_PASSFD)
+ ret = vtysh_do_pass_fd(vty);
+
+ switch (ret) {
case BUFFER_PENDING:
vty_event(VTYSH_WRITE, vty);
break;
case BUFFER_ERROR:
- vty->monitor =
- 0; /* disable monitoring to avoid infinite recursion */
flog_err(EC_LIB_SOCKET, "%s: write error to fd %d, closing",
__func__, vty->fd);
buffer_reset(vty->lbuf);
@@ -2031,6 +2086,14 @@ static int vtysh_flush(struct vty *vty)
return 0;
}
+void vty_pass_fd(struct vty *vty, int fd)
+{
+ if (vty->pass_fd != -1)
+ close(vty->pass_fd);
+
+ vty->pass_fd = fd;
+}
+
static void vtysh_read(struct thread *thread)
{
int ret;
@@ -2050,8 +2113,6 @@ static void vtysh_read(struct thread *thread)
vty_event(VTYSH_READ, vty);
return;
}
- vty->monitor = 0; /* disable monitoring to avoid
- infinite recursion */
flog_err(
EC_LIB_SOCKET,
"%s: read failed on vtysh client fd %d, closing: %s",
@@ -2090,6 +2151,26 @@ static void vtysh_read(struct thread *thread)
printf("vtysh node: %d\n", vty->node);
#endif /* VTYSH_DEBUG */
+ if (vty->pass_fd != -1) {
+ memset(vty->pass_fd_status, 0, 4);
+ vty->pass_fd_status[3] = ret;
+ vty->status = VTY_PASSFD;
+
+ if (!vty->t_write)
+ vty_event(VTYSH_WRITE, vty);
+
+ /* this introduces a "sequence point"
+ * command output is written normally,
+ * read processing is suspended until
+ * buffer is empty
+ * then retcode + FD is written
+ * then normal processing resumes
+ *
+ * => skip vty_event(VTYSH_READ, vty)!
+ */
+ return;
+ }
+
/* hack for asynchronous "write integrated"
* - other commands in "buf" will be ditched
* - input during pending config-write is
@@ -2161,6 +2242,12 @@ void vty_close(struct vty *vty)
THREAD_OFF(vty->t_write);
THREAD_OFF(vty->t_timeout);
+ if (vty->pass_fd != -1) {
+ close(vty->pass_fd);
+ vty->pass_fd = -1;
+ }
+ zlog_live_close(&vty->live_log);
+
/* Flush buffer. */
buffer_flush_all(vty->obuf, vty->wfd);
@@ -2661,8 +2748,9 @@ DEFUN_NOSH (config_who,
struct vty *v;
frr_each (vtys, vty_sessions, v)
- vty_out(vty, "%svty[%d] connected from %s.\n",
- v->config ? "*" : " ", v->fd, v->address);
+ vty_out(vty, "%svty[%d] connected from %s%s.\n",
+ v->config ? "*" : " ", v->fd, v->address,
+ zlog_live_is_null(&v->live_log) ? "" : ", live log");
return CMD_SUCCESS;
}
@@ -2855,35 +2943,56 @@ DEFUN (no_service_advanced_vty,
return CMD_SUCCESS;
}
-DEFUN_NOSH (terminal_monitor,
- terminal_monitor_cmd,
- "terminal monitor",
- "Set terminal line parameters\n"
- "Copy debug output to the current terminal line\n")
+DEFUN_NOSH(terminal_monitor,
+ terminal_monitor_cmd,
+ "terminal monitor [detach]",
+ "Set terminal line parameters\n"
+ "Copy debug output to the current terminal line\n"
+ "Keep logging feed open independent of VTY session\n")
{
- vty->monitor = 1;
+ int fd_ret = -1;
+
+ if (vty->type != VTY_SHELL_SERV) {
+ vty_out(vty, "%% not supported\n");
+ return CMD_WARNING;
+ }
+
+ if (argc == 3) {
+ struct zlog_live_cfg detach_log = {};
+
+ zlog_live_open(&detach_log, LOG_DEBUG, &fd_ret);
+ zlog_live_disown(&detach_log);
+ } else
+ zlog_live_open(&vty->live_log, LOG_DEBUG, &fd_ret);
+
+ if (fd_ret == -1) {
+ vty_out(vty, "%% error opening live log: %m\n");
+ return CMD_WARNING;
+ }
+
+ vty_pass_fd(vty, fd_ret);
return CMD_SUCCESS;
}
-DEFUN_NOSH (terminal_no_monitor,
- terminal_no_monitor_cmd,
- "terminal no monitor",
- "Set terminal line parameters\n"
- NO_STR
- "Copy debug output to the current terminal line\n")
+DEFUN_NOSH(no_terminal_monitor,
+ no_terminal_monitor_cmd,
+ "no terminal monitor",
+ NO_STR
+ "Set terminal line parameters\n"
+ "Copy debug output to the current terminal line\n")
{
- vty->monitor = 0;
+ zlog_live_close(&vty->live_log);
return CMD_SUCCESS;
}
-DEFUN_NOSH (no_terminal_monitor,
- no_terminal_monitor_cmd,
- "no terminal monitor",
- NO_STR
- "Set terminal line parameters\n"
- "Copy debug output to the current terminal line\n")
+DEFUN_NOSH(terminal_no_monitor,
+ terminal_no_monitor_cmd,
+ "terminal no monitor",
+ "Set terminal line parameters\n"
+ NO_STR
+ "Copy debug output to the current terminal line\n")
{
- return terminal_no_monitor(self, vty, argc, argv);
+ return no_terminal_monitor(self, vty, argc, argv);
}
diff --git a/lib/vty.h b/lib/vty.h
index 9ffbce326..e42a3b210 100644
--- a/lib/vty.h
+++ b/lib/vty.h
@@ -34,6 +34,7 @@
#include "qobj.h"
#include "compiler.h"
#include "northbound.h"
+#include "zlog_live.h"
#ifdef __cplusplus
extern "C" {
@@ -161,7 +162,22 @@ struct vty {
unsigned char escape;
/* Current vty status. */
- enum { VTY_NORMAL, VTY_CLOSE, VTY_MORE, VTY_MORELINE } status;
+ enum {
+ VTY_NORMAL,
+ VTY_CLOSE,
+ VTY_MORE,
+ VTY_MORELINE,
+ VTY_PASSFD,
+ } status;
+
+ /* vtysh socket/fd passing (for terminal monitor) */
+ int pass_fd;
+
+ /* CLI command return value (likely CMD_SUCCESS) when pass_fd != -1 */
+ uint8_t pass_fd_status[4];
+
+ /* live logging target / terminal monitor */
+ struct zlog_live_cfg live_log;
/* IAC handling: was the last character received the
IAC (interpret-as-command) escape character (and therefore the next
@@ -186,9 +202,6 @@ struct vty {
/* Configure lines. */
int lines;
- /* Terminal monitor. */
- int monitor;
-
/* Read and write thread. */
struct thread *t_read;
struct thread *t_write;
@@ -329,6 +342,11 @@ extern bool vty_set_include(struct vty *vty, const char *regexp);
*/
extern int vty_json(struct vty *vty, struct json_object *json);
+/* post fd to be passed to the vtysh client
+ * fd is owned by the VTY code after this and will be closed when done
+ */
+extern void vty_pass_fd(struct vty *vty, int fd);
+
extern bool vty_read_config(struct nb_config *config, const char *config_file,
char *config_default_dir);
extern void vty_time_print(struct vty *, int);
diff --git a/lib/yang_wrappers.c b/lib/yang_wrappers.c
index 85aa003db..bee76c6e0 100644
--- a/lib/yang_wrappers.c
+++ b/lib/yang_wrappers.c
@@ -19,6 +19,7 @@
#include <zebra.h>
+#include "base64.h"
#include "log.h"
#include "lib_errors.h"
#include "northbound.h"
@@ -677,6 +678,64 @@ void yang_get_default_string_buf(char *buf, size_t size, const char *xpath_fmt,
}
/*
+ * Primitive type: binary.
+ */
+struct yang_data *yang_data_new_binary(const char *xpath, const char *value,
+ size_t len)
+{
+ char *value_str;
+ struct base64_encodestate s;
+ int cnt;
+ char *c;
+ struct yang_data *data;
+
+ value_str = (char *)malloc(len * 2);
+ base64_init_encodestate(&s);
+ cnt = base64_encode_block(value, len, value_str, &s);
+ c = value_str + cnt;
+ cnt = base64_encode_blockend(c, &s);
+ c += cnt;
+ *c = 0;
+ data = yang_data_new(xpath, value_str);
+ free(value_str);
+ return data;
+}
+
+size_t yang_dnode_get_binary_buf(char *buf, size_t size,
+ const struct lyd_node *dnode,
+ const char *xpath_fmt, ...)
+{
+ const char *canon;
+ size_t cannon_len;
+ size_t decode_len;
+ size_t ret_len;
+ size_t cnt;
+ char *value_str;
+ struct base64_decodestate s;
+
+ canon = YANG_DNODE_XPATH_GET_CANON(dnode, xpath_fmt);
+ cannon_len = strlen(canon);
+ decode_len = cannon_len;
+ value_str = (char *)malloc(decode_len);
+ base64_init_decodestate(&s);
+ cnt = base64_decode_block(canon, cannon_len, value_str, &s);
+
+ ret_len = size > cnt ? cnt : size;
+ memcpy(buf, value_str, ret_len);
+ if (size < cnt) {
+ char xpath[XPATH_MAXLEN];
+
+ yang_dnode_get_path(dnode, xpath, sizeof(xpath));
+ flog_warn(EC_LIB_YANG_DATA_TRUNCATED,
+ "%s: value was truncated [xpath %s]", __func__,
+ xpath);
+ }
+ free(value_str);
+ return ret_len;
+}
+
+
+/*
* Primitive type: empty.
*/
struct yang_data *yang_data_new_empty(const char *xpath)
diff --git a/lib/yang_wrappers.h b/lib/yang_wrappers.h
index d781dfb1e..56b314876 100644
--- a/lib/yang_wrappers.h
+++ b/lib/yang_wrappers.h
@@ -118,6 +118,13 @@ extern const char *yang_get_default_string(const char *xpath_fmt, ...);
extern void yang_get_default_string_buf(char *buf, size_t size,
const char *xpath_fmt, ...);
+/* binary */
+extern struct yang_data *yang_data_new_binary(const char *xpath,
+ const char *value, size_t len);
+extern size_t yang_dnode_get_binary_buf(char *buf, size_t size,
+ const struct lyd_node *dnode,
+ const char *xpath_fmt, ...);
+
/* empty */
extern struct yang_data *yang_data_new_empty(const char *xpath);
extern bool yang_dnode_get_empty(const struct lyd_node *dnode,
diff --git a/lib/zlog.c b/lib/zlog.c
index 1b0751559..85606d262 100644
--- a/lib/zlog.c
+++ b/lib/zlog.c
@@ -908,6 +908,11 @@ size_t zlog_msg_ts_3164(struct zlog_msg *msg, struct fbuf *out, uint32_t flags)
return bputs(out, msg->ts_3164_str);
}
+void zlog_msg_tsraw(struct zlog_msg *msg, struct timespec *ts)
+{
+ memcpy(ts, &msg->ts, sizeof(*ts));
+}
+
void zlog_set_prefix_ec(bool enable)
{
atomic_store_explicit(&zlog_ec, enable, memory_order_relaxed);
diff --git a/lib/zlog.h b/lib/zlog.h
index 6e84fe892..a530c589a 100644
--- a/lib/zlog.h
+++ b/lib/zlog.h
@@ -183,8 +183,11 @@ extern void zlog_msg_args(struct zlog_msg *msg, size_t *hdrlen,
/* default is local time zone */
#define ZLOG_TS_UTC (1 << 10)
+struct timespec;
+
extern size_t zlog_msg_ts(struct zlog_msg *msg, struct fbuf *out,
uint32_t flags);
+extern void zlog_msg_tsraw(struct zlog_msg *msg, struct timespec *ts);
/* "mmm dd hh:mm:ss" for RFC3164 syslog. Only ZLOG_TS_UTC for flags. */
extern size_t zlog_msg_ts_3164(struct zlog_msg *msg, struct fbuf *out,
diff --git a/lib/zlog_live.c b/lib/zlog_live.c
new file mode 100644
index 000000000..fbe0e5ee4
--- /dev/null
+++ b/lib/zlog_live.c
@@ -0,0 +1,245 @@
+/*
+ * Copyright (c) 2019-22 David Lamparter, for NetDEF, Inc.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "zebra.h"
+
+#include "zlog_live.h"
+
+#include "memory.h"
+#include "frrcu.h"
+#include "zlog.h"
+#include "printfrr.h"
+
+DEFINE_MTYPE_STATIC(LOG, LOG_LIVE, "log vtysh live target");
+
+enum {
+ STATE_NORMAL = 0,
+ STATE_FD_DEAD,
+ STATE_DISOWNED,
+};
+
+struct zlt_live {
+ struct zlog_target zt;
+
+ atomic_uint_fast32_t fd;
+ struct rcu_head_close head_close;
+ struct rcu_head head_self;
+
+ atomic_uint_fast32_t state;
+};
+
+static void zlog_live(struct zlog_target *zt, struct zlog_msg *msgs[],
+ size_t nmsgs)
+{
+ struct zlt_live *zte = container_of(zt, struct zlt_live, zt);
+ struct zlog_live_hdr hdrs[nmsgs], *hdr = hdrs;
+ struct mmsghdr mmhs[nmsgs], *mmh = mmhs;
+ struct iovec iovs[nmsgs * 3], *iov = iovs;
+ struct timespec ts;
+ size_t i, textlen;
+ int fd;
+ uint_fast32_t state;
+
+ fd = atomic_load_explicit(&zte->fd, memory_order_relaxed);
+
+ if (fd < 0)
+ return;
+
+ memset(mmhs, 0, sizeof(mmhs));
+ memset(hdrs, 0, sizeof(hdrs));
+
+ for (i = 0; i < nmsgs; i++) {
+ const struct fmt_outpos *argpos;
+ size_t n_argpos, arghdrlen;
+ struct zlog_msg *msg = msgs[i];
+ int prio = zlog_msg_prio(msg);
+
+ if (prio > zt->prio_min)
+ continue;
+
+ zlog_msg_args(msg, &arghdrlen, &n_argpos, &argpos);
+
+ mmh->msg_hdr.msg_iov = iov;
+
+ iov->iov_base = hdr;
+ iov->iov_len = sizeof(*hdr);
+ iov++;
+
+ if (n_argpos) {
+ iov->iov_base = (char *)argpos;
+ iov->iov_len = sizeof(*argpos) * n_argpos;
+ iov++;
+ }
+
+ iov->iov_base = (char *)zlog_msg_text(msg, &textlen);
+ iov->iov_len = textlen;
+ iov++;
+
+ zlog_msg_tsraw(msg, &ts);
+
+ hdr->ts_sec = ts.tv_sec;
+ hdr->ts_nsec = ts.tv_nsec;
+ hdr->prio = zlog_msg_prio(msg);
+ hdr->flags = 0;
+ hdr->textlen = textlen;
+ hdr->arghdrlen = arghdrlen;
+ hdr->n_argpos = n_argpos;
+
+ mmh->msg_hdr.msg_iovlen = iov - mmh->msg_hdr.msg_iov;
+ mmh++;
+ hdr++;
+ }
+
+ size_t msgtotal = mmh - mmhs;
+ ssize_t sent;
+
+ for (size_t msgpos = 0; msgpos < msgtotal; msgpos += sent) {
+ sent = sendmmsg(fd, mmhs + msgpos, msgtotal - msgpos, 0);
+
+ if (sent <= 0)
+ goto out_err;
+ }
+ return;
+
+out_err:
+ fd = atomic_exchange_explicit(&zte->fd, -1, memory_order_relaxed);
+ if (fd < 0)
+ return;
+
+ rcu_close(&zte->head_close, fd);
+ zlog_target_replace(zt, NULL);
+
+ state = STATE_NORMAL;
+ atomic_compare_exchange_strong_explicit(
+ &zte->state, &state, STATE_FD_DEAD, memory_order_relaxed,
+ memory_order_relaxed);
+ if (state == STATE_DISOWNED)
+ rcu_free(MTYPE_LOG_LIVE, zte, head_self);
+}
+
+static void zlog_live_sigsafe(struct zlog_target *zt, const char *text,
+ size_t len)
+{
+ struct zlt_live *zte = container_of(zt, struct zlt_live, zt);
+ struct zlog_live_hdr hdr[1];
+ struct iovec iovs[2], *iov = iovs;
+ struct timespec ts;
+ int fd;
+
+ fd = atomic_load_explicit(&zte->fd, memory_order_relaxed);
+ if (fd < 0)
+ return;
+
+ clock_gettime(CLOCK_MONOTONIC, &ts);
+
+ hdr->ts_sec = ts.tv_sec;
+ hdr->ts_nsec = ts.tv_nsec;
+ hdr->prio = LOG_CRIT;
+ hdr->flags = 0;
+ hdr->textlen = len;
+ hdr->n_argpos = 0;
+
+ iov->iov_base = (char *)hdr;
+ iov->iov_len = sizeof(hdr);
+ iov++;
+
+ iov->iov_base = (char *)text;
+ iov->iov_len = len;
+ iov++;
+
+ writev(fd, iovs, iov - iovs);
+}
+
+void zlog_live_open(struct zlog_live_cfg *cfg, int prio_min, int *other_fd)
+{
+ int sockets[2];
+ struct zlt_live *zte;
+ struct zlog_target *zt;
+
+ if (cfg->target)
+ zlog_live_close(cfg);
+
+ *other_fd = -1;
+ if (prio_min == ZLOG_DISABLED)
+ return;
+
+ /* the only reason for SEQPACKET here is getting close notifications.
+ * otherwise if you open a bunch of vtysh connections with live logs
+ * and close them all, the fds will stick around until we get an error
+ * when trying to log something to them at some later point -- which
+ * eats up fds and might be *much* later for some daemons.
+ */
+ if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, sockets) < 0) {
+ if (socketpair(AF_UNIX, SOCK_DGRAM, 0, sockets) < 0) {
+ zlog_warn("%% could not open socket pair: %m");
+ return;
+ }
+ } else
+ /* SEQPACKET only: try to zap read direction */
+ shutdown(sockets[0], SHUT_RD);
+
+ *other_fd = sockets[1];
+
+ zt = zlog_target_clone(MTYPE_LOG_LIVE, NULL, sizeof(*zte));
+ zte = container_of(zt, struct zlt_live, zt);
+ cfg->target = zte;
+
+ zte->fd = sockets[0];
+ zte->zt.prio_min = prio_min;
+ zte->zt.logfn = zlog_live;
+ zte->zt.logfn_sigsafe = zlog_live_sigsafe;
+
+ zlog_target_replace(NULL, zt);
+}
+
+void zlog_live_close(struct zlog_live_cfg *cfg)
+{
+ struct zlt_live *zte;
+ int fd;
+
+ if (!cfg->target)
+ return;
+
+ zte = cfg->target;
+ cfg->target = NULL;
+
+ fd = atomic_exchange_explicit(&zte->fd, -1, memory_order_relaxed);
+
+ if (fd >= 0) {
+ rcu_close(&zte->head_close, fd);
+ zlog_target_replace(&zte->zt, NULL);
+ }
+ rcu_free(MTYPE_LOG_LIVE, zte, head_self);
+}
+
+void zlog_live_disown(struct zlog_live_cfg *cfg)
+{
+ struct zlt_live *zte;
+ uint_fast32_t state;
+
+ if (!cfg->target)
+ return;
+
+ zte = cfg->target;
+ cfg->target = NULL;
+
+ state = STATE_NORMAL;
+ atomic_compare_exchange_strong_explicit(
+ &zte->state, &state, STATE_DISOWNED, memory_order_relaxed,
+ memory_order_relaxed);
+ if (state == STATE_FD_DEAD)
+ rcu_free(MTYPE_LOG_LIVE, zte, head_self);
+}
diff --git a/lib/zlog_live.h b/lib/zlog_live.h
new file mode 100644
index 000000000..c948baeab
--- /dev/null
+++ b/lib/zlog_live.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2019-22 David Lamparter, for NetDEF, Inc.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _FRR_ZLOG_LIVE_H
+#define _FRR_ZLOG_LIVE_H
+
+#include "printfrr.h"
+
+struct zlog_live_hdr {
+ uint64_t ts_sec;
+ uint32_t ts_nsec;
+ uint32_t prio;
+ uint32_t flags;
+ uint32_t textlen;
+
+ uint32_t arghdrlen;
+ uint32_t n_argpos;
+ struct fmt_outpos argpos[0];
+};
+
+struct zlt_live;
+
+struct zlog_live_cfg {
+ struct zlt_live *target;
+
+ /* nothing else here */
+};
+
+extern void zlog_live_open(struct zlog_live_cfg *cfg, int prio_min,
+ int *other_fd);
+
+static inline bool zlog_live_is_null(struct zlog_live_cfg *cfg)
+{
+ return cfg->target == NULL;
+}
+
+extern void zlog_live_close(struct zlog_live_cfg *cfg);
+extern void zlog_live_disown(struct zlog_live_cfg *cfg);
+
+#endif /* _FRR_ZLOG_5424_H */