diff options
Diffstat (limited to 'lib')
-rw-r--r-- | lib/base64.c | 193 | ||||
-rw-r--r-- | lib/base64.h | 45 | ||||
-rw-r--r-- | lib/checksum.c | 85 | ||||
-rw-r--r-- | lib/checksum.h | 43 | ||||
-rw-r--r-- | lib/subdir.am | 4 | ||||
-rw-r--r-- | lib/vty.c | 171 | ||||
-rw-r--r-- | lib/vty.h | 26 | ||||
-rw-r--r-- | lib/yang_wrappers.c | 59 | ||||
-rw-r--r-- | lib/yang_wrappers.h | 7 | ||||
-rw-r--r-- | lib/zlog.c | 5 | ||||
-rw-r--r-- | lib/zlog.h | 3 | ||||
-rw-r--r-- | lib/zlog_live.c | 245 | ||||
-rw-r--r-- | lib/zlog_live.h | 53 |
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 \ @@ -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); } @@ -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 */ |