2002-12-13 21:15:29 +01:00
|
|
|
/*
|
|
|
|
* Virtual terminal [aka TeletYpe] interface routine.
|
|
|
|
* Copyright (C) 1997, 98 Kunihiro Ishiguro
|
|
|
|
*
|
|
|
|
* This file is part of GNU Zebra.
|
|
|
|
*
|
|
|
|
* GNU Zebra is free software; you can redistribute it and/or modify it
|
|
|
|
* under the terms of the GNU General Public License as published by the
|
|
|
|
* Free Software Foundation; either version 2, or (at your option) any
|
|
|
|
* later version.
|
|
|
|
*
|
|
|
|
* GNU Zebra is distributed in the hope that it will be useful, but
|
|
|
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
* General Public License for more details.
|
|
|
|
*
|
2017-05-13 10:25:29 +02:00
|
|
|
* You should have received a copy of the GNU General Public License along
|
|
|
|
* with this program; see the file COPYING; if not, write to the Free Software
|
|
|
|
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
2002-12-13 21:15:29 +01:00
|
|
|
*/
|
|
|
|
|
|
|
|
#include <zebra.h>
|
|
|
|
|
2018-05-13 02:14:22 +02:00
|
|
|
#include <lib/version.h>
|
|
|
|
#include <sys/types.h>
|
|
|
|
#include <regex.h>
|
2018-06-01 22:13:18 +02:00
|
|
|
#include <stdio.h>
|
2018-05-13 02:14:22 +02:00
|
|
|
|
2002-12-13 21:15:29 +01:00
|
|
|
#include "linklist.h"
|
2003-06-15 03:28:29 +02:00
|
|
|
#include "thread.h"
|
2002-12-13 21:15:29 +01:00
|
|
|
#include "buffer.h"
|
|
|
|
#include "command.h"
|
|
|
|
#include "sockunion.h"
|
|
|
|
#include "memory.h"
|
|
|
|
#include "log.h"
|
|
|
|
#include "prefix.h"
|
|
|
|
#include "filter.h"
|
2003-06-15 03:28:29 +02:00
|
|
|
#include "vty.h"
|
2003-06-04 15:59:38 +02:00
|
|
|
#include "privs.h"
|
2005-02-23 Andrew J. Schorr <ajschorr@alumni.princeton.edu>
* buffer.h: Make the struct buffer and struct buffer_data structures
private by moving them inside buffer.c. Add comments for all
functions. Rename buffer_write as buffer_put (to be more consistent
with the buffer_putc and buffer_putstr functions). Declare a new
buffer_write function that is used to write data to a file descriptor
and/or add it to the buffer queue. Remove unused function
buffer_flush_vty_all. Create a new enum typedef buffer_status_t
to be used as the return code for all buffer_flush* functions
and buffer_write.
* buffer.c: The struct buffer and struct buffer_data declarations
are now private to this file. In conjunction with that, remove
some unnecessary fields: struct buffer (alloc, unused_head,
unused_tail, length), struct buffer_data (prev).
(buffer_data_new) Removed: functionality incorporated into buffer_add.
(buffer_data_free) Removed: use a macro BUFFER_DATA_FREE instead.
(buffer_new) Use calloc instead of malloc + memset(zero).
Supply an appropriate default size if the specified size is 0.
(buffer_free) Eliminate code duplication by calling buffer_reset to
free the contents of the buffer (and remove unused code related
to unused_head).
(buffer_empty,buffer_putc,buffer_putstr) Aesthetic change (make more
compact).
(buffer_reset) Use macro BUFFER_DATA_FREE. No need to set
alloc and length to 0 (these fields have been removed).
(buffer_add) Fix scope to be static. Call XMALLOC directly instead
of calling removed buffer_data_new function. Simplify the logic
(since it's now a singly-linked list instead of doubly-linked).
(buffer_write) Renamed to buffer_put. Change to void, since return
code of 1 was meaningless. No need to adjust length field, since
it has been removed.
(buffer_putw,buffer_flush,buffer_flush_vty_all,buffer_flush_vty)
Remove unused functions.
(buffer_flush_all) Rewrite using buffer_flush_available to eliminate
a possible failure mode if IOV_MAX is less than the number of buffers
on the queue.
(buffer_flush_window) Incorporate logic from buffer_flush_vty.
Log an error message if there is a writev error.
(buffer_flush_available) Be more paranoid: check for case where
buffer is already empty. Use new ERRNO_IO_RETRY macro, and use
new enum for return codes. Simplify deletion logic (since it's
now a singly-linked list).
(buffer_write) New function for use with non-blocking I/O.
* vty.h: Replace the struct vty sb_buffer field with a fixed-size
(5-character) sb_buf field and an sb_len field, since using
a struct buffer was inappropriate for this task. Add some useful
comments about telnet window size negotiation.
* vty.c: Include <arpa/telnet.h> (no longer included by zebra.h).
Remove VTY_OBUF_SIZE (instead use buffer_new default size).
Make telnet_backward_char and telnet_space_char static const.
(vty_out) Replace buffer_write with buffer_put.
(vty_log_out) Check for I/O errors. If fatal, close the vty session.
Consolidate 3 separate writes into a single write call.
(vty_will_echo,vty_command,vty_next_line,vty_previous_line,
vty_end_config,vty_describe_fold,vty_clear_buf,vty_serv_sock_addrinfo,
vty_serv_sock_family,vty_serv_un,vty_use_backup_config,exec_timeout,
vty_config_write,vty_save_cwd) Fix scope to static.
(vty_new) Let buffer_new use its default buffer size.
(vty_write) Fix signature: 2nd arg should be const char *.
Replaced buffer_write with buffer_put.
(vty_telnet_option) Fix minor bug (window height or width greater than
255 was broken). Use sb_buf and sb_len instead of removed sb_buffer
(which was being used improperly).
(vty_read) On error, use ERRNO_IO_RETRY to decide whether it's fatal.
If the error is fatal, call buffer_reset so vty_close does not attempt
to flush the data. Use new sb_buf and sb_len instead of sb_buffer
to store the SB negotiation string.
(vty_flush) When vty->lines is 0, call buffer_flush_available instead
of buffer_flush_window. Look at the return code from buffer_flush
to detect I/O errors (and in that case, log an error message and
close the vty).
(vty_create) Fix scope to static. Initialize sb_len to 0 instead
of creating sb_buffer.
(vty_accept) Set socket nonblocking.
(vtysh_accept) Use new set_nonblocking function instead of calling
fcntl directly.
(vtysh_flush) New function called from vtysh_read (after command
execution) and from vtysh_write. This flushes the buffer
and reacts appropriately to the return code (by closing the vty
or scheduling further flushes).
(vtysh_read) Check whether error is fatal using ERRNO_IO_RETRY.
If not, just try again later. Otherwise, call buffer_reset before
calling vty_close (to avoid trying to flush the buffer in vty_close).
Fix logic to allow case where a command does not arrive atomically
in a single read call by checking for the terminating NUL char.
(vtysh_write) Use new vtysh_flush helper function.
(vty_close) No need to call buffer_empty, just call buffer_flush_all
in any case (it will check whether the buffer is empty).
Do not free sb_buffer (since it has been removed).
(vty_log_fixed) Use writev instead of write.
* zebra.h: Do not include <arpa/telnet.h>, since this is used only
by lib/vty.c.
2005-02-23 16:12:34 +01:00
|
|
|
#include "network.h"
|
2017-05-08 03:06:07 +02:00
|
|
|
#include "libfrr.h"
|
2018-05-13 02:14:22 +02:00
|
|
|
#include "frrstr.h"
|
2018-06-14 15:21:11 +02:00
|
|
|
#include "lib_errors.h"
|
2005-02-23 Andrew J. Schorr <ajschorr@alumni.princeton.edu>
* buffer.h: Make the struct buffer and struct buffer_data structures
private by moving them inside buffer.c. Add comments for all
functions. Rename buffer_write as buffer_put (to be more consistent
with the buffer_putc and buffer_putstr functions). Declare a new
buffer_write function that is used to write data to a file descriptor
and/or add it to the buffer queue. Remove unused function
buffer_flush_vty_all. Create a new enum typedef buffer_status_t
to be used as the return code for all buffer_flush* functions
and buffer_write.
* buffer.c: The struct buffer and struct buffer_data declarations
are now private to this file. In conjunction with that, remove
some unnecessary fields: struct buffer (alloc, unused_head,
unused_tail, length), struct buffer_data (prev).
(buffer_data_new) Removed: functionality incorporated into buffer_add.
(buffer_data_free) Removed: use a macro BUFFER_DATA_FREE instead.
(buffer_new) Use calloc instead of malloc + memset(zero).
Supply an appropriate default size if the specified size is 0.
(buffer_free) Eliminate code duplication by calling buffer_reset to
free the contents of the buffer (and remove unused code related
to unused_head).
(buffer_empty,buffer_putc,buffer_putstr) Aesthetic change (make more
compact).
(buffer_reset) Use macro BUFFER_DATA_FREE. No need to set
alloc and length to 0 (these fields have been removed).
(buffer_add) Fix scope to be static. Call XMALLOC directly instead
of calling removed buffer_data_new function. Simplify the logic
(since it's now a singly-linked list instead of doubly-linked).
(buffer_write) Renamed to buffer_put. Change to void, since return
code of 1 was meaningless. No need to adjust length field, since
it has been removed.
(buffer_putw,buffer_flush,buffer_flush_vty_all,buffer_flush_vty)
Remove unused functions.
(buffer_flush_all) Rewrite using buffer_flush_available to eliminate
a possible failure mode if IOV_MAX is less than the number of buffers
on the queue.
(buffer_flush_window) Incorporate logic from buffer_flush_vty.
Log an error message if there is a writev error.
(buffer_flush_available) Be more paranoid: check for case where
buffer is already empty. Use new ERRNO_IO_RETRY macro, and use
new enum for return codes. Simplify deletion logic (since it's
now a singly-linked list).
(buffer_write) New function for use with non-blocking I/O.
* vty.h: Replace the struct vty sb_buffer field with a fixed-size
(5-character) sb_buf field and an sb_len field, since using
a struct buffer was inappropriate for this task. Add some useful
comments about telnet window size negotiation.
* vty.c: Include <arpa/telnet.h> (no longer included by zebra.h).
Remove VTY_OBUF_SIZE (instead use buffer_new default size).
Make telnet_backward_char and telnet_space_char static const.
(vty_out) Replace buffer_write with buffer_put.
(vty_log_out) Check for I/O errors. If fatal, close the vty session.
Consolidate 3 separate writes into a single write call.
(vty_will_echo,vty_command,vty_next_line,vty_previous_line,
vty_end_config,vty_describe_fold,vty_clear_buf,vty_serv_sock_addrinfo,
vty_serv_sock_family,vty_serv_un,vty_use_backup_config,exec_timeout,
vty_config_write,vty_save_cwd) Fix scope to static.
(vty_new) Let buffer_new use its default buffer size.
(vty_write) Fix signature: 2nd arg should be const char *.
Replaced buffer_write with buffer_put.
(vty_telnet_option) Fix minor bug (window height or width greater than
255 was broken). Use sb_buf and sb_len instead of removed sb_buffer
(which was being used improperly).
(vty_read) On error, use ERRNO_IO_RETRY to decide whether it's fatal.
If the error is fatal, call buffer_reset so vty_close does not attempt
to flush the data. Use new sb_buf and sb_len instead of sb_buffer
to store the SB negotiation string.
(vty_flush) When vty->lines is 0, call buffer_flush_available instead
of buffer_flush_window. Look at the return code from buffer_flush
to detect I/O errors (and in that case, log an error message and
close the vty).
(vty_create) Fix scope to static. Initialize sb_len to 0 instead
of creating sb_buffer.
(vty_accept) Set socket nonblocking.
(vtysh_accept) Use new set_nonblocking function instead of calling
fcntl directly.
(vtysh_flush) New function called from vtysh_read (after command
execution) and from vtysh_write. This flushes the buffer
and reacts appropriately to the return code (by closing the vty
or scheduling further flushes).
(vtysh_read) Check whether error is fatal using ERRNO_IO_RETRY.
If not, just try again later. Otherwise, call buffer_reset before
calling vty_close (to avoid trying to flush the buffer in vty_close).
Fix logic to allow case where a command does not arrive atomically
in a single read call by checking for the terminating NUL char.
(vtysh_write) Use new vtysh_flush helper function.
(vty_close) No need to call buffer_empty, just call buffer_flush_all
in any case (it will check whether the buffer is empty).
Do not free sb_buffer (since it has been removed).
(vty_log_fixed) Use writev instead of write.
* zebra.h: Do not include <arpa/telnet.h>, since this is used only
by lib/vty.c.
2005-02-23 16:12:34 +01:00
|
|
|
|
|
|
|
#include <arpa/telnet.h>
|
2015-05-05 11:04:46 +02:00
|
|
|
#include <termios.h>
|
2002-12-13 21:15:29 +01:00
|
|
|
|
2017-07-17 14:03:14 +02:00
|
|
|
DEFINE_MTYPE_STATIC(LIB, VTY, "VTY")
|
2015-05-29 05:48:31 +02:00
|
|
|
DEFINE_MTYPE_STATIC(LIB, VTY_OUT_BUF, "VTY output buffer")
|
2017-07-17 14:03:14 +02:00
|
|
|
DEFINE_MTYPE_STATIC(LIB, VTY_HIST, "VTY history")
|
2015-05-29 05:48:31 +02:00
|
|
|
|
2002-12-13 21:15:29 +01:00
|
|
|
/* Vty events */
|
2017-07-17 14:03:14 +02:00
|
|
|
enum event {
|
|
|
|
VTY_SERV,
|
|
|
|
VTY_READ,
|
|
|
|
VTY_WRITE,
|
|
|
|
VTY_TIMEOUT_RESET,
|
2002-12-13 21:15:29 +01:00
|
|
|
#ifdef VTYSH
|
2017-07-17 14:03:14 +02:00
|
|
|
VTYSH_SERV,
|
|
|
|
VTYSH_READ,
|
|
|
|
VTYSH_WRITE
|
2002-12-13 21:15:29 +01:00
|
|
|
#endif /* VTYSH */
|
|
|
|
};
|
|
|
|
|
2017-07-17 14:03:14 +02:00
|
|
|
static void vty_event(enum event, int, struct vty *);
|
2002-12-13 21:15:29 +01:00
|
|
|
|
|
|
|
/* Extern host structure from command.c */
|
|
|
|
extern struct host host;
|
2014-06-04 06:53:35 +02:00
|
|
|
|
2002-12-13 21:15:29 +01:00
|
|
|
/* Vector which store each vty structure. */
|
|
|
|
static vector vtyvec;
|
|
|
|
|
|
|
|
/* Vty timeout value. */
|
|
|
|
static unsigned long vty_timeout_val = VTY_TIMEOUT_DEFAULT;
|
|
|
|
|
|
|
|
/* Vty access-class command */
|
|
|
|
static char *vty_accesslist_name = NULL;
|
|
|
|
|
|
|
|
/* Vty access-calss for IPv6. */
|
|
|
|
static char *vty_ipv6_accesslist_name = NULL;
|
|
|
|
|
|
|
|
/* VTY server thread. */
|
2013-02-27 14:47:23 +01:00
|
|
|
static vector Vvty_serv_thread;
|
2002-12-13 21:15:29 +01:00
|
|
|
|
|
|
|
/* Current directory. */
|
|
|
|
char *vty_cwd = NULL;
|
|
|
|
|
|
|
|
/* Configure lock. */
|
|
|
|
static int vty_config;
|
2016-09-27 16:53:25 +02:00
|
|
|
static int vty_config_is_lockless = 0;
|
2002-12-13 21:15:29 +01:00
|
|
|
|
|
|
|
/* Login password check. */
|
|
|
|
static int no_password_check = 0;
|
|
|
|
|
|
|
|
/* Integrated configuration file path */
|
|
|
|
char integrate_default[] = SYSCONFDIR INTEGRATE_DEFAULT_CONFIG;
|
|
|
|
|
2016-05-17 18:19:51 +02:00
|
|
|
static int do_log_commands = 0;
|
2014-06-04 06:53:35 +02:00
|
|
|
|
2010-02-05 09:48:45 +01:00
|
|
|
void vty_frame(struct vty *vty, const char *format, ...)
|
|
|
|
{
|
|
|
|
va_list args;
|
|
|
|
|
|
|
|
va_start(args, format);
|
|
|
|
vsnprintf(vty->frame + vty->frame_pos,
|
2017-08-30 17:23:01 +02:00
|
|
|
sizeof(vty->frame) - vty->frame_pos, format, args);
|
2010-02-05 09:48:45 +01:00
|
|
|
vty->frame_pos = strlen(vty->frame);
|
|
|
|
va_end(args);
|
|
|
|
}
|
|
|
|
|
|
|
|
void vty_endframe(struct vty *vty, const char *endtext)
|
|
|
|
{
|
|
|
|
if (vty->frame_pos == 0 && endtext)
|
|
|
|
vty_out(vty, "%s", endtext);
|
|
|
|
vty->frame_pos = 0;
|
|
|
|
}
|
|
|
|
|
2018-05-13 02:14:22 +02:00
|
|
|
bool vty_set_include(struct vty *vty, const char *regexp)
|
|
|
|
{
|
|
|
|
int errcode;
|
|
|
|
bool ret = true;
|
|
|
|
char errbuf[256];
|
|
|
|
|
2018-06-11 20:02:58 +02:00
|
|
|
if (!regexp) {
|
|
|
|
if (vty->filter) {
|
|
|
|
regfree(&vty->include);
|
|
|
|
vty->filter = false;
|
|
|
|
}
|
2018-05-13 02:14:22 +02:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
errcode = regcomp(&vty->include, regexp,
|
|
|
|
REG_EXTENDED | REG_NEWLINE | REG_NOSUB);
|
|
|
|
if (errcode) {
|
|
|
|
ret = false;
|
|
|
|
regerror(ret, &vty->include, errbuf, sizeof(errbuf));
|
|
|
|
vty_out(vty, "%% Regex compilation error: %s", errbuf);
|
|
|
|
} else {
|
|
|
|
vty->filter = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2017-07-13 17:34:08 +02:00
|
|
|
/* VTY standard output function. */
|
2017-07-17 14:03:14 +02:00
|
|
|
int vty_out(struct vty *vty, const char *format, ...)
|
|
|
|
{
|
|
|
|
va_list args;
|
|
|
|
int len = 0;
|
|
|
|
int size = 1024;
|
|
|
|
char buf[1024];
|
|
|
|
char *p = NULL;
|
2018-05-13 02:14:22 +02:00
|
|
|
char *filtered;
|
2017-07-17 14:03:14 +02:00
|
|
|
|
2010-02-05 09:48:45 +01:00
|
|
|
if (vty->frame_pos) {
|
|
|
|
vty->frame_pos = 0;
|
|
|
|
vty_out(vty, "%s", vty->frame);
|
|
|
|
}
|
|
|
|
|
2018-05-15 00:13:03 +02:00
|
|
|
/* Try to write to initial buffer. */
|
|
|
|
va_start(args, format);
|
|
|
|
len = vsnprintf(buf, sizeof(buf), format, args);
|
|
|
|
va_end(args);
|
2017-07-17 14:03:14 +02:00
|
|
|
|
2018-05-15 00:13:03 +02:00
|
|
|
/* Initial buffer is not enough. */
|
|
|
|
if (len < 0 || len >= size) {
|
|
|
|
while (1) {
|
|
|
|
if (len > -1)
|
|
|
|
size = len + 1;
|
|
|
|
else
|
|
|
|
size = size * 2;
|
2018-05-13 02:14:22 +02:00
|
|
|
|
2018-05-15 00:13:03 +02:00
|
|
|
p = XREALLOC(MTYPE_VTY_OUT_BUF, p, size);
|
|
|
|
if (!p)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
va_start(args, format);
|
|
|
|
len = vsnprintf(p, size, format, args);
|
|
|
|
va_end(args);
|
|
|
|
|
|
|
|
if (len > -1 && len < size)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2018-05-13 02:14:22 +02:00
|
|
|
|
2018-05-15 00:13:03 +02:00
|
|
|
/* When initial buffer is enough to store all output. */
|
|
|
|
if (!p)
|
|
|
|
p = buf;
|
|
|
|
|
|
|
|
/* filter buffer */
|
|
|
|
if (vty->filter) {
|
2018-06-06 21:09:44 +02:00
|
|
|
vector lines = frrstr_split_vec(p, "\n");
|
|
|
|
|
|
|
|
/* Place first value in the cache */
|
|
|
|
char *firstline = vector_slot(lines, 0);
|
|
|
|
buffer_put(vty->lbuf, (uint8_t *) firstline, strlen(firstline));
|
|
|
|
|
|
|
|
/* If our split returned more than one entry, time to filter */
|
|
|
|
if (vector_active(lines) > 1) {
|
|
|
|
/*
|
|
|
|
* returned string is MTYPE_TMP so it matches the MTYPE
|
|
|
|
* of everything else in the vector
|
|
|
|
*/
|
|
|
|
char *bstr = buffer_getstr(vty->lbuf);
|
|
|
|
buffer_reset(vty->lbuf);
|
|
|
|
XFREE(MTYPE_TMP, lines->index[0]);
|
|
|
|
vector_set_index(lines, 0, bstr);
|
|
|
|
frrstr_filter_vec(lines, &vty->include);
|
|
|
|
vector_compact(lines);
|
|
|
|
/*
|
|
|
|
* Consider the string "foo\n". If the regex is an empty string
|
|
|
|
* and the line ended with a newline, then the vector will look
|
|
|
|
* like:
|
|
|
|
*
|
|
|
|
* [0]: 'foo'
|
|
|
|
* [1]: ''
|
|
|
|
*
|
|
|
|
* If the regex isn't empty, the vector will look like:
|
|
|
|
*
|
|
|
|
* [0]: 'foo'
|
|
|
|
*
|
|
|
|
* In this case we'd like to preserve the newline, so we add
|
|
|
|
* the empty string [1] as in the first example.
|
|
|
|
*/
|
|
|
|
if (p[strlen(p) - 1] == '\n' && vector_active(lines) > 0
|
|
|
|
&& strlen(vector_slot(lines, vector_active(lines) - 1)))
|
|
|
|
vector_set(lines, XSTRDUP(MTYPE_TMP, ""));
|
|
|
|
|
|
|
|
filtered = frrstr_join_vec(lines, "\n");
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
filtered = NULL;
|
|
|
|
}
|
2018-05-29 19:13:51 +02:00
|
|
|
|
2018-05-15 00:13:03 +02:00
|
|
|
frrstr_strvec_free(lines);
|
2018-06-06 21:09:44 +02:00
|
|
|
|
2018-05-15 00:13:03 +02:00
|
|
|
} else {
|
|
|
|
filtered = p;
|
|
|
|
}
|
2017-07-17 14:03:14 +02:00
|
|
|
|
2018-06-06 21:09:44 +02:00
|
|
|
if (!filtered)
|
|
|
|
goto done;
|
|
|
|
|
2018-05-15 00:13:03 +02:00
|
|
|
switch (vty->type) {
|
|
|
|
case VTY_TERM:
|
|
|
|
/* print with crlf replacement */
|
|
|
|
buffer_put_crlf(vty->obuf, (uint8_t *)filtered,
|
|
|
|
strlen(filtered));
|
|
|
|
break;
|
|
|
|
case VTY_SHELL:
|
|
|
|
fprintf(vty->of, "%s", filtered);
|
|
|
|
fflush(vty->of);
|
|
|
|
break;
|
|
|
|
case VTY_SHELL_SERV:
|
|
|
|
case VTY_FILE:
|
|
|
|
default:
|
|
|
|
/* print without crlf replacement */
|
|
|
|
buffer_put(vty->obuf, (uint8_t *)filtered, strlen(filtered));
|
|
|
|
break;
|
2017-07-17 14:03:14 +02:00
|
|
|
}
|
|
|
|
|
2018-06-06 21:09:44 +02:00
|
|
|
done:
|
|
|
|
|
|
|
|
if (vty->filter && filtered)
|
2018-05-15 00:13:03 +02:00
|
|
|
XFREE(MTYPE_TMP, filtered);
|
|
|
|
|
|
|
|
/* If p is not different with buf, it is allocated buffer. */
|
|
|
|
if (p != buf)
|
|
|
|
XFREE(MTYPE_VTY_OUT_BUF, p);
|
|
|
|
|
2017-07-17 14:03:14 +02:00
|
|
|
return len;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int vty_log_out(struct vty *vty, const char *level,
|
|
|
|
const char *proto_str, const char *format,
|
|
|
|
struct timestamp_control *ctl, va_list va)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
int len;
|
|
|
|
char buf[1024];
|
|
|
|
|
|
|
|
if (!ctl->already_rendered) {
|
|
|
|
ctl->len = quagga_timestamp(ctl->precision, ctl->buf,
|
|
|
|
sizeof(ctl->buf));
|
|
|
|
ctl->already_rendered = 1;
|
|
|
|
}
|
|
|
|
if (ctl->len + 1 >= sizeof(buf))
|
|
|
|
return -1;
|
|
|
|
memcpy(buf, ctl->buf, len = ctl->len);
|
|
|
|
buf[len++] = ' ';
|
|
|
|
buf[len] = '\0';
|
|
|
|
|
|
|
|
if (level)
|
|
|
|
ret = snprintf(buf + len, sizeof(buf) - len, "%s: %s: ", level,
|
|
|
|
proto_str);
|
|
|
|
else
|
|
|
|
ret = snprintf(buf + len, sizeof(buf) - len, "%s: ", proto_str);
|
|
|
|
if ((ret < 0) || ((size_t)(len += ret) >= sizeof(buf)))
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
if (((ret = vsnprintf(buf + len, sizeof(buf) - len, format, va)) < 0)
|
|
|
|
|| ((size_t)((len += ret) + 2) > sizeof(buf)))
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
buf[len++] = '\r';
|
|
|
|
buf[len++] = '\n';
|
|
|
|
|
|
|
|
if (write(vty->wfd, buf, len) < 0) {
|
|
|
|
if (ERRNO_IO_RETRY(errno))
|
|
|
|
/* Kernel buffer is full, probably too much debugging
|
|
|
|
output, so just
|
|
|
|
drop the data and ignore. */
|
|
|
|
return -1;
|
|
|
|
/* Fatal I/O error. */
|
|
|
|
vty->monitor =
|
|
|
|
0; /* disable monitoring to avoid infinite recursion */
|
|
|
|
zlog_warn("%s: write failed to vty client fd %d, closing: %s",
|
|
|
|
__func__, vty->fd, safe_strerror(errno));
|
|
|
|
buffer_reset(vty->obuf);
|
2018-06-06 21:09:44 +02:00
|
|
|
buffer_reset(vty->lbuf);
|
2017-07-17 14:03:14 +02:00
|
|
|
/* cannot call vty_close, because a parent routine may still try
|
|
|
|
to access the vty struct */
|
|
|
|
vty->status = VTY_CLOSE;
|
|
|
|
shutdown(vty->fd, SHUT_RDWR);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
return 0;
|
2002-12-13 21:15:29 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Output current time to the vty. */
|
2017-07-17 14:03:14 +02:00
|
|
|
void vty_time_print(struct vty *vty, int cr)
|
2002-12-13 21:15:29 +01:00
|
|
|
{
|
2017-07-17 14:03:14 +02:00
|
|
|
char buf[QUAGGA_TIMESTAMP_LEN];
|
2016-09-20 01:46:51 +02:00
|
|
|
|
2017-07-17 14:03:14 +02:00
|
|
|
if (quagga_timestamp(0, buf, sizeof(buf)) == 0) {
|
|
|
|
zlog_info("quagga_timestamp error");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (cr)
|
|
|
|
vty_out(vty, "%s\n", buf);
|
|
|
|
else
|
|
|
|
vty_out(vty, "%s ", buf);
|
2002-12-13 21:15:29 +01:00
|
|
|
|
2017-07-17 14:03:14 +02:00
|
|
|
return;
|
2002-12-13 21:15:29 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Say hello to vty interface. */
|
2017-07-17 14:03:14 +02:00
|
|
|
void vty_hello(struct vty *vty)
|
|
|
|
{
|
|
|
|
if (host.motdfile) {
|
|
|
|
FILE *f;
|
|
|
|
char buf[4096];
|
|
|
|
|
|
|
|
f = fopen(host.motdfile, "r");
|
|
|
|
if (f) {
|
|
|
|
while (fgets(buf, sizeof(buf), f)) {
|
|
|
|
char *s;
|
|
|
|
/* work backwards to ignore trailling isspace()
|
|
|
|
*/
|
|
|
|
for (s = buf + strlen(buf);
|
|
|
|
(s > buf) && isspace((int)*(s - 1)); s--)
|
|
|
|
;
|
|
|
|
*s = '\0';
|
|
|
|
vty_out(vty, "%s\n", buf);
|
|
|
|
}
|
|
|
|
fclose(f);
|
|
|
|
} else
|
|
|
|
vty_out(vty, "MOTD file not found\n");
|
|
|
|
} else if (host.motd)
|
|
|
|
vty_out(vty, "%s", host.motd);
|
2002-12-13 21:15:29 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Put out prompt and wait input from user. */
|
2017-07-17 14:03:14 +02:00
|
|
|
static void vty_prompt(struct vty *vty)
|
2002-12-13 21:15:29 +01:00
|
|
|
{
|
2017-07-17 14:03:14 +02:00
|
|
|
if (vty->type == VTY_TERM) {
|
2017-08-30 17:23:01 +02:00
|
|
|
vty_out(vty, cmd_prompt(vty->node), cmd_hostname_get());
|
2017-07-17 14:03:14 +02:00
|
|
|
}
|
2002-12-13 21:15:29 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Send WILL TELOPT_ECHO to remote server. */
|
2017-07-17 14:03:14 +02:00
|
|
|
static void vty_will_echo(struct vty *vty)
|
2002-12-13 21:15:29 +01:00
|
|
|
{
|
2017-07-17 14:03:14 +02:00
|
|
|
unsigned char cmd[] = {IAC, WILL, TELOPT_ECHO, '\0'};
|
|
|
|
vty_out(vty, "%s", cmd);
|
2002-12-13 21:15:29 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Make suppress Go-Ahead telnet option. */
|
2017-07-17 14:03:14 +02:00
|
|
|
static void vty_will_suppress_go_ahead(struct vty *vty)
|
2002-12-13 21:15:29 +01:00
|
|
|
{
|
2017-07-17 14:03:14 +02:00
|
|
|
unsigned char cmd[] = {IAC, WILL, TELOPT_SGA, '\0'};
|
|
|
|
vty_out(vty, "%s", cmd);
|
2002-12-13 21:15:29 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Make don't use linemode over telnet. */
|
2017-07-17 14:03:14 +02:00
|
|
|
static void vty_dont_linemode(struct vty *vty)
|
2002-12-13 21:15:29 +01:00
|
|
|
{
|
2017-07-17 14:03:14 +02:00
|
|
|
unsigned char cmd[] = {IAC, DONT, TELOPT_LINEMODE, '\0'};
|
|
|
|
vty_out(vty, "%s", cmd);
|
2002-12-13 21:15:29 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Use window size. */
|
2017-07-17 14:03:14 +02:00
|
|
|
static void vty_do_window_size(struct vty *vty)
|
2002-12-13 21:15:29 +01:00
|
|
|
{
|
2017-07-17 14:03:14 +02:00
|
|
|
unsigned char cmd[] = {IAC, DO, TELOPT_NAWS, '\0'};
|
|
|
|
vty_out(vty, "%s", cmd);
|
2002-12-13 21:15:29 +01:00
|
|
|
}
|
|
|
|
|
2017-07-17 14:03:14 +02:00
|
|
|
#if 0 /* Currently not used. */
|
2002-12-13 21:15:29 +01:00
|
|
|
/* Make don't use lflow vty interface. */
|
|
|
|
static void
|
|
|
|
vty_dont_lflow_ahead (struct vty *vty)
|
|
|
|
{
|
2004-06-11 13:27:03 +02:00
|
|
|
unsigned char cmd[] = { IAC, DONT, TELOPT_LFLOW, '\0' };
|
2002-12-13 21:15:29 +01:00
|
|
|
vty_out (vty, "%s", cmd);
|
|
|
|
}
|
|
|
|
#endif /* 0 */
|
|
|
|
|
|
|
|
/* Authentication of vty */
|
2017-07-17 14:03:14 +02:00
|
|
|
static void vty_auth(struct vty *vty, char *buf)
|
|
|
|
{
|
|
|
|
char *passwd = NULL;
|
|
|
|
enum node_type next_node = 0;
|
|
|
|
int fail;
|
|
|
|
char *crypt(const char *, const char *);
|
|
|
|
|
|
|
|
switch (vty->node) {
|
|
|
|
case AUTH_NODE:
|
|
|
|
if (host.encrypt)
|
|
|
|
passwd = host.password_encrypt;
|
|
|
|
else
|
|
|
|
passwd = host.password;
|
|
|
|
if (host.advanced)
|
|
|
|
next_node = host.enable ? VIEW_NODE : ENABLE_NODE;
|
|
|
|
else
|
|
|
|
next_node = VIEW_NODE;
|
|
|
|
break;
|
|
|
|
case AUTH_ENABLE_NODE:
|
|
|
|
if (host.encrypt)
|
|
|
|
passwd = host.enable_encrypt;
|
|
|
|
else
|
|
|
|
passwd = host.enable;
|
|
|
|
next_node = ENABLE_NODE;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (passwd) {
|
|
|
|
if (host.encrypt)
|
|
|
|
fail = strcmp(crypt(buf, passwd), passwd);
|
|
|
|
else
|
|
|
|
fail = strcmp(buf, passwd);
|
|
|
|
} else
|
|
|
|
fail = 1;
|
|
|
|
|
|
|
|
if (!fail) {
|
|
|
|
vty->fail = 0;
|
|
|
|
vty->node = next_node; /* Success ! */
|
|
|
|
} else {
|
|
|
|
vty->fail++;
|
|
|
|
if (vty->fail >= 3) {
|
|
|
|
if (vty->node == AUTH_NODE) {
|
|
|
|
vty_out(vty,
|
|
|
|
"%% Bad passwords, too many failures!\n");
|
|
|
|
vty->status = VTY_CLOSE;
|
|
|
|
} else {
|
|
|
|
/* AUTH_ENABLE_NODE */
|
|
|
|
vty->fail = 0;
|
|
|
|
vty_out(vty,
|
|
|
|
"%% Bad enable passwords, too many failures!\n");
|
|
|
|
vty->status = VTY_CLOSE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2002-12-13 21:15:29 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Command execution over the vty interface. */
|
2017-07-17 14:03:14 +02:00
|
|
|
static int vty_command(struct vty *vty, char *buf)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
const char *protocolname;
|
|
|
|
char *cp = NULL;
|
|
|
|
|
2018-07-03 15:39:50 +02:00
|
|
|
assert(vty);
|
|
|
|
|
2017-07-17 14:03:14 +02:00
|
|
|
/*
|
|
|
|
* Log non empty command lines
|
|
|
|
*/
|
|
|
|
if (do_log_commands)
|
|
|
|
cp = buf;
|
|
|
|
if (cp != NULL) {
|
|
|
|
/* Skip white spaces. */
|
|
|
|
while (isspace((int)*cp) && *cp != '\0')
|
|
|
|
cp++;
|
|
|
|
}
|
|
|
|
if (cp != NULL && *cp != '\0') {
|
|
|
|
unsigned i;
|
|
|
|
char vty_str[VTY_BUFSIZ];
|
|
|
|
char prompt_str[VTY_BUFSIZ];
|
|
|
|
|
|
|
|
/* format the base vty info */
|
|
|
|
snprintf(vty_str, sizeof(vty_str), "vty[??]@%s", vty->address);
|
2018-07-03 15:39:50 +02:00
|
|
|
|
|
|
|
for (i = 0; i < vector_active(vtyvec); i++)
|
|
|
|
if (vty == vector_slot(vtyvec, i)) {
|
|
|
|
snprintf(vty_str, sizeof(vty_str), "vty[%d]@%s",
|
|
|
|
i, vty->address);
|
|
|
|
break;
|
|
|
|
}
|
2017-07-17 14:03:14 +02:00
|
|
|
|
|
|
|
/* format the prompt */
|
|
|
|
snprintf(prompt_str, sizeof(prompt_str), cmd_prompt(vty->node),
|
|
|
|
vty_str);
|
|
|
|
|
|
|
|
/* now log the command */
|
2018-06-14 15:02:49 +02:00
|
|
|
zlog_notice("%s%s", prompt_str, buf);
|
2017-07-17 14:03:14 +02:00
|
|
|
}
|
2002-12-13 21:15:29 +01:00
|
|
|
|
2005-04-16 19:11:24 +02:00
|
|
|
#ifdef CONSUMED_TIME_CHECK
|
2017-07-17 14:03:14 +02:00
|
|
|
{
|
|
|
|
RUSAGE_T before;
|
|
|
|
RUSAGE_T after;
|
|
|
|
unsigned long realtime, cputime;
|
2005-04-16 19:11:24 +02:00
|
|
|
|
2017-07-17 14:03:14 +02:00
|
|
|
GETRUSAGE(&before);
|
2005-04-16 19:11:24 +02:00
|
|
|
#endif /* CONSUMED_TIME_CHECK */
|
|
|
|
|
2018-05-13 02:14:22 +02:00
|
|
|
ret = cmd_execute(vty, buf, NULL, 0);
|
2002-12-13 21:15:29 +01:00
|
|
|
|
2017-07-17 14:03:14 +02:00
|
|
|
/* Get the name of the protocol if any */
|
|
|
|
protocolname = frr_protoname;
|
2016-09-20 01:46:51 +02:00
|
|
|
|
2005-04-16 19:11:24 +02:00
|
|
|
#ifdef CONSUMED_TIME_CHECK
|
2017-07-17 14:03:14 +02:00
|
|
|
GETRUSAGE(&after);
|
|
|
|
if ((realtime = thread_consumed_time(&after, &before, &cputime))
|
|
|
|
> CONSUMED_TIME_CHECK)
|
|
|
|
/* Warn about CPU hog that must be fixed. */
|
|
|
|
zlog_warn(
|
|
|
|
"SLOW COMMAND: command took %lums (cpu time %lums): %s",
|
|
|
|
realtime / 1000, cputime / 1000, buf);
|
|
|
|
}
|
2005-04-16 19:11:24 +02:00
|
|
|
#endif /* CONSUMED_TIME_CHECK */
|
|
|
|
|
2017-07-17 14:03:14 +02:00
|
|
|
if (ret != CMD_SUCCESS)
|
|
|
|
switch (ret) {
|
|
|
|
case CMD_WARNING:
|
|
|
|
if (vty->type == VTY_FILE)
|
|
|
|
vty_out(vty, "Warning...\n");
|
|
|
|
break;
|
|
|
|
case CMD_ERR_AMBIGUOUS:
|
|
|
|
vty_out(vty, "%% Ambiguous command.\n");
|
|
|
|
break;
|
|
|
|
case CMD_ERR_NO_MATCH:
|
|
|
|
vty_out(vty, "%% [%s] Unknown command: %s\n",
|
|
|
|
protocolname, buf);
|
|
|
|
break;
|
|
|
|
case CMD_ERR_INCOMPLETE:
|
|
|
|
vty_out(vty, "%% Command incomplete.\n");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
2002-12-13 21:15:29 +01:00
|
|
|
}
|
2014-06-04 06:53:35 +02:00
|
|
|
|
2005-02-23 Andrew J. Schorr <ajschorr@alumni.princeton.edu>
* buffer.h: Make the struct buffer and struct buffer_data structures
private by moving them inside buffer.c. Add comments for all
functions. Rename buffer_write as buffer_put (to be more consistent
with the buffer_putc and buffer_putstr functions). Declare a new
buffer_write function that is used to write data to a file descriptor
and/or add it to the buffer queue. Remove unused function
buffer_flush_vty_all. Create a new enum typedef buffer_status_t
to be used as the return code for all buffer_flush* functions
and buffer_write.
* buffer.c: The struct buffer and struct buffer_data declarations
are now private to this file. In conjunction with that, remove
some unnecessary fields: struct buffer (alloc, unused_head,
unused_tail, length), struct buffer_data (prev).
(buffer_data_new) Removed: functionality incorporated into buffer_add.
(buffer_data_free) Removed: use a macro BUFFER_DATA_FREE instead.
(buffer_new) Use calloc instead of malloc + memset(zero).
Supply an appropriate default size if the specified size is 0.
(buffer_free) Eliminate code duplication by calling buffer_reset to
free the contents of the buffer (and remove unused code related
to unused_head).
(buffer_empty,buffer_putc,buffer_putstr) Aesthetic change (make more
compact).
(buffer_reset) Use macro BUFFER_DATA_FREE. No need to set
alloc and length to 0 (these fields have been removed).
(buffer_add) Fix scope to be static. Call XMALLOC directly instead
of calling removed buffer_data_new function. Simplify the logic
(since it's now a singly-linked list instead of doubly-linked).
(buffer_write) Renamed to buffer_put. Change to void, since return
code of 1 was meaningless. No need to adjust length field, since
it has been removed.
(buffer_putw,buffer_flush,buffer_flush_vty_all,buffer_flush_vty)
Remove unused functions.
(buffer_flush_all) Rewrite using buffer_flush_available to eliminate
a possible failure mode if IOV_MAX is less than the number of buffers
on the queue.
(buffer_flush_window) Incorporate logic from buffer_flush_vty.
Log an error message if there is a writev error.
(buffer_flush_available) Be more paranoid: check for case where
buffer is already empty. Use new ERRNO_IO_RETRY macro, and use
new enum for return codes. Simplify deletion logic (since it's
now a singly-linked list).
(buffer_write) New function for use with non-blocking I/O.
* vty.h: Replace the struct vty sb_buffer field with a fixed-size
(5-character) sb_buf field and an sb_len field, since using
a struct buffer was inappropriate for this task. Add some useful
comments about telnet window size negotiation.
* vty.c: Include <arpa/telnet.h> (no longer included by zebra.h).
Remove VTY_OBUF_SIZE (instead use buffer_new default size).
Make telnet_backward_char and telnet_space_char static const.
(vty_out) Replace buffer_write with buffer_put.
(vty_log_out) Check for I/O errors. If fatal, close the vty session.
Consolidate 3 separate writes into a single write call.
(vty_will_echo,vty_command,vty_next_line,vty_previous_line,
vty_end_config,vty_describe_fold,vty_clear_buf,vty_serv_sock_addrinfo,
vty_serv_sock_family,vty_serv_un,vty_use_backup_config,exec_timeout,
vty_config_write,vty_save_cwd) Fix scope to static.
(vty_new) Let buffer_new use its default buffer size.
(vty_write) Fix signature: 2nd arg should be const char *.
Replaced buffer_write with buffer_put.
(vty_telnet_option) Fix minor bug (window height or width greater than
255 was broken). Use sb_buf and sb_len instead of removed sb_buffer
(which was being used improperly).
(vty_read) On error, use ERRNO_IO_RETRY to decide whether it's fatal.
If the error is fatal, call buffer_reset so vty_close does not attempt
to flush the data. Use new sb_buf and sb_len instead of sb_buffer
to store the SB negotiation string.
(vty_flush) When vty->lines is 0, call buffer_flush_available instead
of buffer_flush_window. Look at the return code from buffer_flush
to detect I/O errors (and in that case, log an error message and
close the vty).
(vty_create) Fix scope to static. Initialize sb_len to 0 instead
of creating sb_buffer.
(vty_accept) Set socket nonblocking.
(vtysh_accept) Use new set_nonblocking function instead of calling
fcntl directly.
(vtysh_flush) New function called from vtysh_read (after command
execution) and from vtysh_write. This flushes the buffer
and reacts appropriately to the return code (by closing the vty
or scheduling further flushes).
(vtysh_read) Check whether error is fatal using ERRNO_IO_RETRY.
If not, just try again later. Otherwise, call buffer_reset before
calling vty_close (to avoid trying to flush the buffer in vty_close).
Fix logic to allow case where a command does not arrive atomically
in a single read call by checking for the terminating NUL char.
(vtysh_write) Use new vtysh_flush helper function.
(vty_close) No need to call buffer_empty, just call buffer_flush_all
in any case (it will check whether the buffer is empty).
Do not free sb_buffer (since it has been removed).
(vty_log_fixed) Use writev instead of write.
* zebra.h: Do not include <arpa/telnet.h>, since this is used only
by lib/vty.c.
2005-02-23 16:12:34 +01:00
|
|
|
static const char telnet_backward_char = 0x08;
|
|
|
|
static const char telnet_space_char = ' ';
|
2002-12-13 21:15:29 +01:00
|
|
|
|
|
|
|
/* Basic function to write buffer to vty. */
|
2017-07-17 14:03:14 +02:00
|
|
|
static void vty_write(struct vty *vty, const char *buf, size_t nbytes)
|
2002-12-13 21:15:29 +01:00
|
|
|
{
|
2017-07-17 14:03:14 +02:00
|
|
|
if ((vty->node == AUTH_NODE) || (vty->node == AUTH_ENABLE_NODE))
|
|
|
|
return;
|
2002-12-13 21:15:29 +01:00
|
|
|
|
2017-07-17 14:03:14 +02:00
|
|
|
/* Should we do buffering here ? And make vty_flush (vty) ? */
|
|
|
|
buffer_put(vty->obuf, buf, nbytes);
|
2002-12-13 21:15:29 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Basic function to insert character into vty. */
|
2017-07-17 14:03:14 +02:00
|
|
|
static void vty_self_insert(struct vty *vty, char c)
|
2002-12-13 21:15:29 +01:00
|
|
|
{
|
2017-07-17 14:03:14 +02:00
|
|
|
int i;
|
|
|
|
int length;
|
2002-12-13 21:15:29 +01:00
|
|
|
|
2017-07-17 14:03:14 +02:00
|
|
|
if (vty->length + 1 >= VTY_BUFSIZ)
|
|
|
|
return;
|
2017-01-11 00:33:50 +01:00
|
|
|
|
2017-07-17 14:03:14 +02:00
|
|
|
length = vty->length - vty->cp;
|
|
|
|
memmove(&vty->buf[vty->cp + 1], &vty->buf[vty->cp], length);
|
|
|
|
vty->buf[vty->cp] = c;
|
2002-12-13 21:15:29 +01:00
|
|
|
|
2017-07-17 14:03:14 +02:00
|
|
|
vty_write(vty, &vty->buf[vty->cp], length + 1);
|
|
|
|
for (i = 0; i < length; i++)
|
|
|
|
vty_write(vty, &telnet_backward_char, 1);
|
2002-12-13 21:15:29 +01:00
|
|
|
|
2017-07-17 14:03:14 +02:00
|
|
|
vty->cp++;
|
|
|
|
vty->length++;
|
2017-01-18 07:09:26 +01:00
|
|
|
|
2017-07-17 14:03:14 +02:00
|
|
|
vty->buf[vty->length] = '\0';
|
2002-12-13 21:15:29 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Self insert character 'c' in overwrite mode. */
|
2017-07-17 14:03:14 +02:00
|
|
|
static void vty_self_insert_overwrite(struct vty *vty, char c)
|
2002-12-13 21:15:29 +01:00
|
|
|
{
|
2017-07-17 14:03:14 +02:00
|
|
|
if (vty->cp == vty->length) {
|
|
|
|
vty_self_insert(vty, c);
|
|
|
|
return;
|
|
|
|
}
|
2002-12-13 21:15:29 +01:00
|
|
|
|
2017-07-17 14:03:14 +02:00
|
|
|
vty->buf[vty->cp++] = c;
|
|
|
|
vty_write(vty, &c, 1);
|
2002-12-13 21:15:29 +01:00
|
|
|
}
|
|
|
|
|
2017-01-11 00:33:50 +01:00
|
|
|
/**
|
|
|
|
* Insert a string into vty->buf at the current cursor position.
|
|
|
|
*
|
|
|
|
* If the resultant string would be larger than VTY_BUFSIZ it is
|
|
|
|
* truncated to fit.
|
|
|
|
*/
|
2017-07-17 14:03:14 +02:00
|
|
|
static void vty_insert_word_overwrite(struct vty *vty, char *str)
|
2002-12-13 21:15:29 +01:00
|
|
|
{
|
2017-07-17 14:03:14 +02:00
|
|
|
if (vty->cp == VTY_BUFSIZ)
|
|
|
|
return;
|
2017-01-18 07:09:26 +01:00
|
|
|
|
2017-07-17 14:03:14 +02:00
|
|
|
size_t nwrite = MIN((int)strlen(str), VTY_BUFSIZ - vty->cp - 1);
|
|
|
|
memcpy(&vty->buf[vty->cp], str, nwrite);
|
|
|
|
vty->cp += nwrite;
|
|
|
|
vty->length = MAX(vty->cp, vty->length);
|
|
|
|
vty->buf[vty->length] = '\0';
|
|
|
|
vty_write(vty, str, nwrite);
|
2002-12-13 21:15:29 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Forward character. */
|
2017-07-17 14:03:14 +02:00
|
|
|
static void vty_forward_char(struct vty *vty)
|
2002-12-13 21:15:29 +01:00
|
|
|
{
|
2017-07-17 14:03:14 +02:00
|
|
|
if (vty->cp < vty->length) {
|
|
|
|
vty_write(vty, &vty->buf[vty->cp], 1);
|
|
|
|
vty->cp++;
|
|
|
|
}
|
2002-12-13 21:15:29 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Backward character. */
|
2017-07-17 14:03:14 +02:00
|
|
|
static void vty_backward_char(struct vty *vty)
|
2002-12-13 21:15:29 +01:00
|
|
|
{
|
2017-07-17 14:03:14 +02:00
|
|
|
if (vty->cp > 0) {
|
|
|
|
vty->cp--;
|
|
|
|
vty_write(vty, &telnet_backward_char, 1);
|
|
|
|
}
|
2002-12-13 21:15:29 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Move to the beginning of the line. */
|
2017-07-17 14:03:14 +02:00
|
|
|
static void vty_beginning_of_line(struct vty *vty)
|
2002-12-13 21:15:29 +01:00
|
|
|
{
|
2017-07-17 14:03:14 +02:00
|
|
|
while (vty->cp)
|
|
|
|
vty_backward_char(vty);
|
2002-12-13 21:15:29 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Move to the end of the line. */
|
2017-07-17 14:03:14 +02:00
|
|
|
static void vty_end_of_line(struct vty *vty)
|
2002-12-13 21:15:29 +01:00
|
|
|
{
|
2017-07-17 14:03:14 +02:00
|
|
|
while (vty->cp < vty->length)
|
|
|
|
vty_forward_char(vty);
|
2002-12-13 21:15:29 +01:00
|
|
|
}
|
|
|
|
|
2017-07-17 14:03:14 +02:00
|
|
|
static void vty_kill_line_from_beginning(struct vty *);
|
|
|
|
static void vty_redraw_line(struct vty *);
|
2002-12-13 21:15:29 +01:00
|
|
|
|
|
|
|
/* Print command line history. This function is called from
|
|
|
|
vty_next_line and vty_previous_line. */
|
2017-07-17 14:03:14 +02:00
|
|
|
static void vty_history_print(struct vty *vty)
|
2002-12-13 21:15:29 +01:00
|
|
|
{
|
2017-07-17 14:03:14 +02:00
|
|
|
int length;
|
2002-12-13 21:15:29 +01:00
|
|
|
|
2017-07-17 14:03:14 +02:00
|
|
|
vty_kill_line_from_beginning(vty);
|
2002-12-13 21:15:29 +01:00
|
|
|
|
2017-07-17 14:03:14 +02:00
|
|
|
/* Get previous line from history buffer */
|
|
|
|
length = strlen(vty->hist[vty->hp]);
|
|
|
|
memcpy(vty->buf, vty->hist[vty->hp], length);
|
|
|
|
vty->cp = vty->length = length;
|
|
|
|
vty->buf[vty->length] = '\0';
|
2002-12-13 21:15:29 +01:00
|
|
|
|
2017-07-17 14:03:14 +02:00
|
|
|
/* Redraw current line */
|
|
|
|
vty_redraw_line(vty);
|
2002-12-13 21:15:29 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Show next command line history. */
|
2017-07-17 14:03:14 +02:00
|
|
|
static void vty_next_line(struct vty *vty)
|
2002-12-13 21:15:29 +01:00
|
|
|
{
|
2017-07-17 14:03:14 +02:00
|
|
|
int try_index;
|
2002-12-13 21:15:29 +01:00
|
|
|
|
2017-07-17 14:03:14 +02:00
|
|
|
if (vty->hp == vty->hindex)
|
|
|
|
return;
|
2002-12-13 21:15:29 +01:00
|
|
|
|
2017-07-17 14:03:14 +02:00
|
|
|
/* Try is there history exist or not. */
|
|
|
|
try_index = vty->hp;
|
|
|
|
if (try_index == (VTY_MAXHIST - 1))
|
|
|
|
try_index = 0;
|
|
|
|
else
|
|
|
|
try_index++;
|
2002-12-13 21:15:29 +01:00
|
|
|
|
2017-07-17 14:03:14 +02:00
|
|
|
/* If there is not history return. */
|
|
|
|
if (vty->hist[try_index] == NULL)
|
|
|
|
return;
|
|
|
|
else
|
|
|
|
vty->hp = try_index;
|
2002-12-13 21:15:29 +01:00
|
|
|
|
2017-07-17 14:03:14 +02:00
|
|
|
vty_history_print(vty);
|
2002-12-13 21:15:29 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Show previous command line history. */
|
2017-07-17 14:03:14 +02:00
|
|
|
static void vty_previous_line(struct vty *vty)
|
2002-12-13 21:15:29 +01:00
|
|
|
{
|
2017-07-17 14:03:14 +02:00
|
|
|
int try_index;
|
2002-12-13 21:15:29 +01:00
|
|
|
|
2017-07-17 14:03:14 +02:00
|
|
|
try_index = vty->hp;
|
|
|
|
if (try_index == 0)
|
|
|
|
try_index = VTY_MAXHIST - 1;
|
|
|
|
else
|
|
|
|
try_index--;
|
2002-12-13 21:15:29 +01:00
|
|
|
|
2017-07-17 14:03:14 +02:00
|
|
|
if (vty->hist[try_index] == NULL)
|
|
|
|
return;
|
|
|
|
else
|
|
|
|
vty->hp = try_index;
|
2002-12-13 21:15:29 +01:00
|
|
|
|
2017-07-17 14:03:14 +02:00
|
|
|
vty_history_print(vty);
|
2002-12-13 21:15:29 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/* This function redraw all of the command line character. */
|
2017-07-17 14:03:14 +02:00
|
|
|
static void vty_redraw_line(struct vty *vty)
|
2002-12-13 21:15:29 +01:00
|
|
|
{
|
2017-07-17 14:03:14 +02:00
|
|
|
vty_write(vty, vty->buf, vty->length);
|
|
|
|
vty->cp = vty->length;
|
2002-12-13 21:15:29 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Forward word. */
|
2017-07-17 14:03:14 +02:00
|
|
|
static void vty_forward_word(struct vty *vty)
|
2002-12-13 21:15:29 +01:00
|
|
|
{
|
2017-07-17 14:03:14 +02:00
|
|
|
while (vty->cp != vty->length && vty->buf[vty->cp] != ' ')
|
|
|
|
vty_forward_char(vty);
|
2016-09-20 01:46:51 +02:00
|
|
|
|
2017-07-17 14:03:14 +02:00
|
|
|
while (vty->cp != vty->length && vty->buf[vty->cp] == ' ')
|
|
|
|
vty_forward_char(vty);
|
2002-12-13 21:15:29 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Backward word without skipping training space. */
|
2017-07-17 14:03:14 +02:00
|
|
|
static void vty_backward_pure_word(struct vty *vty)
|
2002-12-13 21:15:29 +01:00
|
|
|
{
|
2017-07-17 14:03:14 +02:00
|
|
|
while (vty->cp > 0 && vty->buf[vty->cp - 1] != ' ')
|
|
|
|
vty_backward_char(vty);
|
2002-12-13 21:15:29 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Backward word. */
|
2017-07-17 14:03:14 +02:00
|
|
|
static void vty_backward_word(struct vty *vty)
|
2002-12-13 21:15:29 +01:00
|
|
|
{
|
2017-07-17 14:03:14 +02:00
|
|
|
while (vty->cp > 0 && vty->buf[vty->cp - 1] == ' ')
|
|
|
|
vty_backward_char(vty);
|
2002-12-13 21:15:29 +01:00
|
|
|
|
2017-07-17 14:03:14 +02:00
|
|
|
while (vty->cp > 0 && vty->buf[vty->cp - 1] != ' ')
|
|
|
|
vty_backward_char(vty);
|
2002-12-13 21:15:29 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/* When '^D' is typed at the beginning of the line we move to the down
|
|
|
|
level. */
|
2017-07-17 14:03:14 +02:00
|
|
|
static void vty_down_level(struct vty *vty)
|
2002-12-13 21:15:29 +01:00
|
|
|
{
|
2017-07-17 14:03:14 +02:00
|
|
|
vty_out(vty, "\n");
|
|
|
|
cmd_exit(vty);
|
|
|
|
vty_prompt(vty);
|
|
|
|
vty->cp = 0;
|
2002-12-13 21:15:29 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/* When '^Z' is received from vty, move down to the enable mode. */
|
2017-07-17 14:03:14 +02:00
|
|
|
static void vty_end_config(struct vty *vty)
|
|
|
|
{
|
|
|
|
vty_out(vty, "\n");
|
|
|
|
|
|
|
|
switch (vty->node) {
|
|
|
|
case VIEW_NODE:
|
|
|
|
case ENABLE_NODE:
|
|
|
|
/* Nothing to do. */
|
|
|
|
break;
|
|
|
|
case CONFIG_NODE:
|
|
|
|
case INTERFACE_NODE:
|
2017-08-02 01:16:28 +02:00
|
|
|
case PW_NODE:
|
2017-07-17 14:03:14 +02:00
|
|
|
case ZEBRA_NODE:
|
|
|
|
case RIP_NODE:
|
|
|
|
case RIPNG_NODE:
|
|
|
|
case EIGRP_NODE:
|
|
|
|
case BGP_NODE:
|
|
|
|
case BGP_VPNV4_NODE:
|
|
|
|
case BGP_VPNV6_NODE:
|
|
|
|
case BGP_VRF_POLICY_NODE:
|
|
|
|
case BGP_VNC_DEFAULTS_NODE:
|
|
|
|
case BGP_VNC_NVE_GROUP_NODE:
|
|
|
|
case BGP_VNC_L2_GROUP_NODE:
|
|
|
|
case BGP_IPV4_NODE:
|
|
|
|
case BGP_IPV4M_NODE:
|
|
|
|
case BGP_IPV4L_NODE:
|
|
|
|
case BGP_IPV6_NODE:
|
|
|
|
case BGP_IPV6M_NODE:
|
|
|
|
case BGP_EVPN_NODE:
|
|
|
|
case BGP_IPV6L_NODE:
|
|
|
|
case RMAP_NODE:
|
pbrd: Add PBR to FRR
This is an implementation of PBR for FRR.
This implemenation uses a combination of rules and
tables to determine how packets will flow.
PBR introduces a new concept of 'nexthop-groups' to
specify a group of nexthops that will be used for
ecmp. Nexthop-groups are specified on the cli via:
nexthop-group DONNA
nexthop 192.168.208.1
nexthop 192.168.209.1
nexthop 192.168.210.1
!
PBR sees the nexthop-group and installs these as a default
route with these nexthops starting at table 10000
robot# show pbr nexthop-groups
Nexthop-Group: DONNA Table: 10001 Valid: 1 Installed: 1
Valid: 1 nexthop 192.168.209.1
Valid: 1 nexthop 192.168.210.1
Valid: 1 nexthop 192.168.208.1
I have also introduced the ability to specify a table
in a 'show ip route table XXX' to see the specified tables.
robot# show ip route table 10001
Codes: K - kernel route, C - connected, S - static, R - RIP,
O - OSPF, I - IS-IS, B - BGP, P - PIM, E - EIGRP, N - NHRP,
T - Table, v - VNC, V - VNC-Direct, A - Babel, D - SHARP,
F - PBR,
> - selected route, * - FIB route
F>* 0.0.0.0/0 [0/0] via 192.168.208.1, enp0s8, 00:14:25
* via 192.168.209.1, enp0s9, 00:14:25
* via 192.168.210.1, enp0s10, 00:14:25
PBR tracks PBR-MAPS via the pbr-map command:
!
pbr-map EVA seq 10
match src-ip 4.3.4.0/24
set nexthop-group DONNA
!
pbr-map EVA seq 20
match dst-ip 4.3.5.0/24
set nexthop-group DONNA
!
pbr-maps can have 'match src-ip <prefix>' and 'match dst-ip <prefix>'
to affect decisions about incoming packets. Additionally if you
only have one nexthop to use for a pbr-map you do not need
to setup a nexthop-group and can specify 'set nexthop XXXX'.
To apply the pbr-map to an incoming interface you do this:
interface enp0s10
pbr-policy EVA
!
When a pbr-map is applied to interfaces it can be installed
into the kernel as a rule:
[sharpd@robot frr1]$ ip rule show
0: from all lookup local
309: from 4.3.4.0/24 iif enp0s10 lookup 10001
319: from all to 4.3.5.0/24 iif enp0s10 lookup 10001
1000: from all lookup [l3mdev-table]
32766: from all lookup main
32767: from all lookup default
[sharpd@robot frr1]$ ip route show table 10001
default proto pbr metric 20
nexthop via 192.168.208.1 dev enp0s8 weight 1
nexthop via 192.168.209.1 dev enp0s9 weight 1
nexthop via 192.168.210.1 dev enp0s10 weight 1
The linux kernel now will use the rules and tables to properly
apply these policies.
Signed-off-by: Donald Sharp <sharpd@cumulusnetworks.com>
Signed-off-by: Don Slice <dslice@cumulusnetworks.com>
Signed-off-by: Quentin Young <qlyoung@cumulusnetworks.com>
2018-01-23 19:11:36 +01:00
|
|
|
case PBRMAP_NODE:
|
2017-07-17 14:03:14 +02:00
|
|
|
case OSPF_NODE:
|
|
|
|
case OSPF6_NODE:
|
|
|
|
case LDP_NODE:
|
|
|
|
case LDP_IPV4_NODE:
|
|
|
|
case LDP_IPV6_NODE:
|
|
|
|
case LDP_IPV4_IFACE_NODE:
|
|
|
|
case LDP_IPV6_IFACE_NODE:
|
|
|
|
case LDP_L2VPN_NODE:
|
|
|
|
case LDP_PSEUDOWIRE_NODE:
|
|
|
|
case ISIS_NODE:
|
|
|
|
case KEYCHAIN_NODE:
|
|
|
|
case KEYCHAIN_KEY_NODE:
|
|
|
|
case VTY_NODE:
|
|
|
|
case BGP_EVPN_VNI_NODE:
|
2018-06-27 18:26:06 +02:00
|
|
|
case BFD_NODE:
|
|
|
|
case BFD_PEER_NODE:
|
2017-07-17 14:03:14 +02:00
|
|
|
vty_config_unlock(vty);
|
|
|
|
vty->node = ENABLE_NODE;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
/* Unknown node, we have to ignore it. */
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
vty_prompt(vty);
|
|
|
|
vty->cp = 0;
|
2002-12-13 21:15:29 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Delete a charcter at the current point. */
|
2017-07-17 14:03:14 +02:00
|
|
|
static void vty_delete_char(struct vty *vty)
|
2002-12-13 21:15:29 +01:00
|
|
|
{
|
2017-07-17 14:03:14 +02:00
|
|
|
int i;
|
|
|
|
int size;
|
2002-12-13 21:15:29 +01:00
|
|
|
|
2017-07-17 14:03:14 +02:00
|
|
|
if (vty->length == 0) {
|
|
|
|
vty_down_level(vty);
|
|
|
|
return;
|
|
|
|
}
|
2002-12-13 21:15:29 +01:00
|
|
|
|
2017-07-17 14:03:14 +02:00
|
|
|
if (vty->cp == vty->length)
|
|
|
|
return; /* completion need here? */
|
2002-12-13 21:15:29 +01:00
|
|
|
|
2017-07-17 14:03:14 +02:00
|
|
|
size = vty->length - vty->cp;
|
2002-12-13 21:15:29 +01:00
|
|
|
|
2017-07-17 14:03:14 +02:00
|
|
|
vty->length--;
|
|
|
|
memmove(&vty->buf[vty->cp], &vty->buf[vty->cp + 1], size - 1);
|
|
|
|
vty->buf[vty->length] = '\0';
|
2016-09-20 01:46:51 +02:00
|
|
|
|
2017-07-17 14:03:14 +02:00
|
|
|
if (vty->node == AUTH_NODE || vty->node == AUTH_ENABLE_NODE)
|
|
|
|
return;
|
2002-12-13 21:15:29 +01:00
|
|
|
|
2017-07-17 14:03:14 +02:00
|
|
|
vty_write(vty, &vty->buf[vty->cp], size - 1);
|
|
|
|
vty_write(vty, &telnet_space_char, 1);
|
2002-12-13 21:15:29 +01:00
|
|
|
|
2017-07-17 14:03:14 +02:00
|
|
|
for (i = 0; i < size; i++)
|
|
|
|
vty_write(vty, &telnet_backward_char, 1);
|
2002-12-13 21:15:29 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Delete a character before the point. */
|
2017-07-17 14:03:14 +02:00
|
|
|
static void vty_delete_backward_char(struct vty *vty)
|
2002-12-13 21:15:29 +01:00
|
|
|
{
|
2017-07-17 14:03:14 +02:00
|
|
|
if (vty->cp == 0)
|
|
|
|
return;
|
2002-12-13 21:15:29 +01:00
|
|
|
|
2017-07-17 14:03:14 +02:00
|
|
|
vty_backward_char(vty);
|
|
|
|
vty_delete_char(vty);
|
2002-12-13 21:15:29 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Kill rest of line from current point. */
|
2017-07-17 14:03:14 +02:00
|
|
|
static void vty_kill_line(struct vty *vty)
|
2002-12-13 21:15:29 +01:00
|
|
|
{
|
2017-07-17 14:03:14 +02:00
|
|
|
int i;
|
|
|
|
int size;
|
2002-12-13 21:15:29 +01:00
|
|
|
|
2017-07-17 14:03:14 +02:00
|
|
|
size = vty->length - vty->cp;
|
2016-09-20 01:46:51 +02:00
|
|
|
|
2017-07-17 14:03:14 +02:00
|
|
|
if (size == 0)
|
|
|
|
return;
|
2002-12-13 21:15:29 +01:00
|
|
|
|
2017-07-17 14:03:14 +02:00
|
|
|
for (i = 0; i < size; i++)
|
|
|
|
vty_write(vty, &telnet_space_char, 1);
|
|
|
|
for (i = 0; i < size; i++)
|
|
|
|
vty_write(vty, &telnet_backward_char, 1);
|
2002-12-13 21:15:29 +01:00
|
|
|
|
2017-07-17 14:03:14 +02:00
|
|
|
memset(&vty->buf[vty->cp], 0, size);
|
|
|
|
vty->length = vty->cp;
|
2002-12-13 21:15:29 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Kill line from the beginning. */
|
2017-07-17 14:03:14 +02:00
|
|
|
static void vty_kill_line_from_beginning(struct vty *vty)
|
2002-12-13 21:15:29 +01:00
|
|
|
{
|
2017-07-17 14:03:14 +02:00
|
|
|
vty_beginning_of_line(vty);
|
|
|
|
vty_kill_line(vty);
|
2002-12-13 21:15:29 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Delete a word before the point. */
|
2017-07-17 14:03:14 +02:00
|
|
|
static void vty_forward_kill_word(struct vty *vty)
|
2002-12-13 21:15:29 +01:00
|
|
|
{
|
2017-07-17 14:03:14 +02:00
|
|
|
while (vty->cp != vty->length && vty->buf[vty->cp] == ' ')
|
|
|
|
vty_delete_char(vty);
|
|
|
|
while (vty->cp != vty->length && vty->buf[vty->cp] != ' ')
|
|
|
|
vty_delete_char(vty);
|
2002-12-13 21:15:29 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Delete a word before the point. */
|
2017-07-17 14:03:14 +02:00
|
|
|
static void vty_backward_kill_word(struct vty *vty)
|
2002-12-13 21:15:29 +01:00
|
|
|
{
|
2017-07-17 14:03:14 +02:00
|
|
|
while (vty->cp > 0 && vty->buf[vty->cp - 1] == ' ')
|
|
|
|
vty_delete_backward_char(vty);
|
|
|
|
while (vty->cp > 0 && vty->buf[vty->cp - 1] != ' ')
|
|
|
|
vty_delete_backward_char(vty);
|
2002-12-13 21:15:29 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Transpose chars before or at the point. */
|
2017-07-17 14:03:14 +02:00
|
|
|
static void vty_transpose_chars(struct vty *vty)
|
2002-12-13 21:15:29 +01:00
|
|
|
{
|
2017-07-17 14:03:14 +02:00
|
|
|
char c1, c2;
|
2002-12-13 21:15:29 +01:00
|
|
|
|
2017-07-17 14:03:14 +02:00
|
|
|
/* If length is short or point is near by the beginning of line then
|
|
|
|
return. */
|
|
|
|
if (vty->length < 2 || vty->cp < 1)
|
|
|
|
return;
|
2002-12-13 21:15:29 +01:00
|
|
|
|
2017-07-17 14:03:14 +02:00
|
|
|
/* In case of point is located at the end of the line. */
|
|
|
|
if (vty->cp == vty->length) {
|
|
|
|
c1 = vty->buf[vty->cp - 1];
|
|
|
|
c2 = vty->buf[vty->cp - 2];
|
2002-12-13 21:15:29 +01:00
|
|
|
|
2017-07-17 14:03:14 +02:00
|
|
|
vty_backward_char(vty);
|
|
|
|
vty_backward_char(vty);
|
|
|
|
vty_self_insert_overwrite(vty, c1);
|
|
|
|
vty_self_insert_overwrite(vty, c2);
|
|
|
|
} else {
|
|
|
|
c1 = vty->buf[vty->cp];
|
|
|
|
c2 = vty->buf[vty->cp - 1];
|
2002-12-13 21:15:29 +01:00
|
|
|
|
2017-07-17 14:03:14 +02:00
|
|
|
vty_backward_char(vty);
|
|
|
|
vty_self_insert_overwrite(vty, c1);
|
|
|
|
vty_self_insert_overwrite(vty, c2);
|
|
|
|
}
|
2002-12-13 21:15:29 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Do completion at vty interface. */
|
2017-07-17 14:03:14 +02:00
|
|
|
static void vty_complete_command(struct vty *vty)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
int ret;
|
|
|
|
char **matched = NULL;
|
|
|
|
vector vline;
|
|
|
|
|
|
|
|
if (vty->node == AUTH_NODE || vty->node == AUTH_ENABLE_NODE)
|
|
|
|
return;
|
|
|
|
|
|
|
|
vline = cmd_make_strvec(vty->buf);
|
|
|
|
if (vline == NULL)
|
|
|
|
return;
|
|
|
|
|
|
|
|
/* In case of 'help \t'. */
|
|
|
|
if (isspace((int)vty->buf[vty->length - 1]))
|
|
|
|
vector_set(vline, NULL);
|
|
|
|
|
|
|
|
matched = cmd_complete_command(vline, vty, &ret);
|
|
|
|
|
|
|
|
cmd_free_strvec(vline);
|
|
|
|
|
|
|
|
vty_out(vty, "\n");
|
|
|
|
switch (ret) {
|
|
|
|
case CMD_ERR_AMBIGUOUS:
|
|
|
|
vty_out(vty, "%% Ambiguous command.\n");
|
|
|
|
vty_prompt(vty);
|
|
|
|
vty_redraw_line(vty);
|
|
|
|
break;
|
|
|
|
case CMD_ERR_NO_MATCH:
|
|
|
|
/* vty_out (vty, "%% There is no matched command.\n"); */
|
|
|
|
vty_prompt(vty);
|
|
|
|
vty_redraw_line(vty);
|
|
|
|
break;
|
|
|
|
case CMD_COMPLETE_FULL_MATCH:
|
|
|
|
if (!matched[0]) {
|
|
|
|
/* 2016-11-28 equinox -- need to debug, SEGV here */
|
|
|
|
vty_out(vty, "%% CLI BUG: FULL_MATCH with NULL str\n");
|
|
|
|
vty_prompt(vty);
|
|
|
|
vty_redraw_line(vty);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
vty_prompt(vty);
|
|
|
|
vty_redraw_line(vty);
|
|
|
|
vty_backward_pure_word(vty);
|
|
|
|
vty_insert_word_overwrite(vty, matched[0]);
|
|
|
|
vty_self_insert(vty, ' ');
|
|
|
|
XFREE(MTYPE_COMPLETION, matched[0]);
|
|
|
|
break;
|
|
|
|
case CMD_COMPLETE_MATCH:
|
|
|
|
vty_prompt(vty);
|
|
|
|
vty_redraw_line(vty);
|
|
|
|
vty_backward_pure_word(vty);
|
|
|
|
vty_insert_word_overwrite(vty, matched[0]);
|
|
|
|
XFREE(MTYPE_COMPLETION, matched[0]);
|
|
|
|
break;
|
|
|
|
case CMD_COMPLETE_LIST_MATCH:
|
|
|
|
for (i = 0; matched[i] != NULL; i++) {
|
|
|
|
if (i != 0 && ((i % 6) == 0))
|
|
|
|
vty_out(vty, "\n");
|
|
|
|
vty_out(vty, "%-10s ", matched[i]);
|
|
|
|
XFREE(MTYPE_COMPLETION, matched[i]);
|
|
|
|
}
|
|
|
|
vty_out(vty, "\n");
|
|
|
|
|
|
|
|
vty_prompt(vty);
|
|
|
|
vty_redraw_line(vty);
|
|
|
|
break;
|
|
|
|
case CMD_ERR_NOTHING_TODO:
|
|
|
|
vty_prompt(vty);
|
|
|
|
vty_redraw_line(vty);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (matched)
|
|
|
|
XFREE(MTYPE_TMP, matched);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void vty_describe_fold(struct vty *vty, int cmd_width,
|
|
|
|
unsigned int desc_width, struct cmd_token *token)
|
|
|
|
{
|
|
|
|
char *buf;
|
|
|
|
const char *cmd, *p;
|
|
|
|
int pos;
|
|
|
|
|
|
|
|
cmd = token->text;
|
|
|
|
|
|
|
|
if (desc_width <= 0) {
|
|
|
|
vty_out(vty, " %-*s %s\n", cmd_width, cmd, token->desc);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
buf = XCALLOC(MTYPE_TMP, strlen(token->desc) + 1);
|
|
|
|
|
|
|
|
for (p = token->desc; strlen(p) > desc_width; p += pos + 1) {
|
|
|
|
for (pos = desc_width; pos > 0; pos--)
|
|
|
|
if (*(p + pos) == ' ')
|
|
|
|
break;
|
|
|
|
|
|
|
|
if (pos == 0)
|
|
|
|
break;
|
|
|
|
|
|
|
|
strncpy(buf, p, pos);
|
|
|
|
buf[pos] = '\0';
|
|
|
|
vty_out(vty, " %-*s %s\n", cmd_width, cmd, buf);
|
|
|
|
|
|
|
|
cmd = "";
|
|
|
|
}
|
|
|
|
|
|
|
|
vty_out(vty, " %-*s %s\n", cmd_width, cmd, p);
|
|
|
|
|
|
|
|
XFREE(MTYPE_TMP, buf);
|
2002-12-13 21:15:29 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Describe matched command function. */
|
2017-07-17 14:03:14 +02:00
|
|
|
static void vty_describe_command(struct vty *vty)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
vector vline;
|
|
|
|
vector describe;
|
|
|
|
unsigned int i, width, desc_width;
|
|
|
|
struct cmd_token *token, *token_cr = NULL;
|
|
|
|
|
|
|
|
vline = cmd_make_strvec(vty->buf);
|
|
|
|
|
|
|
|
/* In case of '> ?'. */
|
|
|
|
if (vline == NULL) {
|
|
|
|
vline = vector_init(1);
|
|
|
|
vector_set(vline, NULL);
|
|
|
|
} else if (isspace((int)vty->buf[vty->length - 1]))
|
|
|
|
vector_set(vline, NULL);
|
|
|
|
|
|
|
|
describe = cmd_describe_command(vline, vty, &ret);
|
|
|
|
|
|
|
|
vty_out(vty, "\n");
|
|
|
|
|
|
|
|
/* Ambiguous error. */
|
|
|
|
switch (ret) {
|
|
|
|
case CMD_ERR_AMBIGUOUS:
|
|
|
|
vty_out(vty, "%% Ambiguous command.\n");
|
|
|
|
goto out;
|
|
|
|
break;
|
|
|
|
case CMD_ERR_NO_MATCH:
|
|
|
|
vty_out(vty, "%% There is no matched command.\n");
|
|
|
|
goto out;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Get width of command string. */
|
|
|
|
width = 0;
|
|
|
|
for (i = 0; i < vector_active(describe); i++)
|
|
|
|
if ((token = vector_slot(describe, i)) != NULL) {
|
|
|
|
unsigned int len;
|
|
|
|
|
|
|
|
if (token->text[0] == '\0')
|
|
|
|
continue;
|
|
|
|
|
|
|
|
len = strlen(token->text);
|
|
|
|
|
|
|
|
if (width < len)
|
|
|
|
width = len;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Get width of description string. */
|
|
|
|
desc_width = vty->width - (width + 6);
|
|
|
|
|
|
|
|
/* Print out description. */
|
|
|
|
for (i = 0; i < vector_active(describe); i++)
|
|
|
|
if ((token = vector_slot(describe, i)) != NULL) {
|
|
|
|
if (token->text[0] == '\0')
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (strcmp(token->text, CMD_CR_TEXT) == 0) {
|
|
|
|
token_cr = token;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!token->desc)
|
|
|
|
vty_out(vty, " %-s\n", token->text);
|
|
|
|
else if (desc_width >= strlen(token->desc))
|
|
|
|
vty_out(vty, " %-*s %s\n", width, token->text,
|
|
|
|
token->desc);
|
|
|
|
else
|
|
|
|
vty_describe_fold(vty, width, desc_width,
|
|
|
|
token);
|
|
|
|
|
|
|
|
if (IS_VARYING_TOKEN(token->type)) {
|
|
|
|
const char *ref = vector_slot(
|
|
|
|
vline, vector_active(vline) - 1);
|
|
|
|
|
|
|
|
vector varcomps = vector_init(VECTOR_MIN_SIZE);
|
|
|
|
cmd_variable_complete(token, ref, varcomps);
|
|
|
|
|
|
|
|
if (vector_active(varcomps) > 0) {
|
|
|
|
char *ac = cmd_variable_comp2str(
|
|
|
|
varcomps, vty->width);
|
|
|
|
vty_out(vty, "%s\n", ac);
|
|
|
|
XFREE(MTYPE_TMP, ac);
|
|
|
|
}
|
|
|
|
|
|
|
|
vector_free(varcomps);
|
|
|
|
}
|
2002-12-13 21:15:29 +01:00
|
|
|
#if 0
|
2017-07-13 19:42:42 +02:00
|
|
|
vty_out (vty, " %-*s %s\n", width
|
2016-09-20 01:46:51 +02:00
|
|
|
desc->cmd[0] == '.' ? desc->cmd + 1 : desc->cmd,
|
2017-07-13 19:42:42 +02:00
|
|
|
desc->str ? desc->str : "");
|
2002-12-13 21:15:29 +01:00
|
|
|
#endif /* 0 */
|
2017-07-17 14:03:14 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if ((token = token_cr)) {
|
|
|
|
if (!token->desc)
|
|
|
|
vty_out(vty, " %-s\n", token->text);
|
|
|
|
else if (desc_width >= strlen(token->desc))
|
|
|
|
vty_out(vty, " %-*s %s\n", width, token->text,
|
|
|
|
token->desc);
|
|
|
|
else
|
|
|
|
vty_describe_fold(vty, width, desc_width, token);
|
|
|
|
}
|
2002-12-13 21:15:29 +01:00
|
|
|
|
2006-05-13 01:22:01 +02:00
|
|
|
out:
|
2017-07-17 14:03:14 +02:00
|
|
|
cmd_free_strvec(vline);
|
|
|
|
if (describe)
|
|
|
|
vector_free(describe);
|
2002-12-13 21:15:29 +01:00
|
|
|
|
2017-07-17 14:03:14 +02:00
|
|
|
vty_prompt(vty);
|
|
|
|
vty_redraw_line(vty);
|
2002-12-13 21:15:29 +01:00
|
|
|
}
|
|
|
|
|
2017-07-17 14:03:14 +02:00
|
|
|
static void vty_clear_buf(struct vty *vty)
|
2002-12-13 21:15:29 +01:00
|
|
|
{
|
2017-07-17 14:03:14 +02:00
|
|
|
memset(vty->buf, 0, vty->max);
|
2002-12-13 21:15:29 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/* ^C stop current input and do not add command line to the history. */
|
2017-07-17 14:03:14 +02:00
|
|
|
static void vty_stop_input(struct vty *vty)
|
|
|
|
{
|
|
|
|
vty->cp = vty->length = 0;
|
|
|
|
vty_clear_buf(vty);
|
|
|
|
vty_out(vty, "\n");
|
|
|
|
|
|
|
|
switch (vty->node) {
|
|
|
|
case VIEW_NODE:
|
|
|
|
case ENABLE_NODE:
|
|
|
|
/* Nothing to do. */
|
|
|
|
break;
|
|
|
|
case CONFIG_NODE:
|
|
|
|
case INTERFACE_NODE:
|
2017-08-02 01:16:28 +02:00
|
|
|
case PW_NODE:
|
2017-07-17 14:03:14 +02:00
|
|
|
case ZEBRA_NODE:
|
|
|
|
case RIP_NODE:
|
|
|
|
case RIPNG_NODE:
|
|
|
|
case EIGRP_NODE:
|
|
|
|
case BGP_NODE:
|
|
|
|
case RMAP_NODE:
|
pbrd: Add PBR to FRR
This is an implementation of PBR for FRR.
This implemenation uses a combination of rules and
tables to determine how packets will flow.
PBR introduces a new concept of 'nexthop-groups' to
specify a group of nexthops that will be used for
ecmp. Nexthop-groups are specified on the cli via:
nexthop-group DONNA
nexthop 192.168.208.1
nexthop 192.168.209.1
nexthop 192.168.210.1
!
PBR sees the nexthop-group and installs these as a default
route with these nexthops starting at table 10000
robot# show pbr nexthop-groups
Nexthop-Group: DONNA Table: 10001 Valid: 1 Installed: 1
Valid: 1 nexthop 192.168.209.1
Valid: 1 nexthop 192.168.210.1
Valid: 1 nexthop 192.168.208.1
I have also introduced the ability to specify a table
in a 'show ip route table XXX' to see the specified tables.
robot# show ip route table 10001
Codes: K - kernel route, C - connected, S - static, R - RIP,
O - OSPF, I - IS-IS, B - BGP, P - PIM, E - EIGRP, N - NHRP,
T - Table, v - VNC, V - VNC-Direct, A - Babel, D - SHARP,
F - PBR,
> - selected route, * - FIB route
F>* 0.0.0.0/0 [0/0] via 192.168.208.1, enp0s8, 00:14:25
* via 192.168.209.1, enp0s9, 00:14:25
* via 192.168.210.1, enp0s10, 00:14:25
PBR tracks PBR-MAPS via the pbr-map command:
!
pbr-map EVA seq 10
match src-ip 4.3.4.0/24
set nexthop-group DONNA
!
pbr-map EVA seq 20
match dst-ip 4.3.5.0/24
set nexthop-group DONNA
!
pbr-maps can have 'match src-ip <prefix>' and 'match dst-ip <prefix>'
to affect decisions about incoming packets. Additionally if you
only have one nexthop to use for a pbr-map you do not need
to setup a nexthop-group and can specify 'set nexthop XXXX'.
To apply the pbr-map to an incoming interface you do this:
interface enp0s10
pbr-policy EVA
!
When a pbr-map is applied to interfaces it can be installed
into the kernel as a rule:
[sharpd@robot frr1]$ ip rule show
0: from all lookup local
309: from 4.3.4.0/24 iif enp0s10 lookup 10001
319: from all to 4.3.5.0/24 iif enp0s10 lookup 10001
1000: from all lookup [l3mdev-table]
32766: from all lookup main
32767: from all lookup default
[sharpd@robot frr1]$ ip route show table 10001
default proto pbr metric 20
nexthop via 192.168.208.1 dev enp0s8 weight 1
nexthop via 192.168.209.1 dev enp0s9 weight 1
nexthop via 192.168.210.1 dev enp0s10 weight 1
The linux kernel now will use the rules and tables to properly
apply these policies.
Signed-off-by: Donald Sharp <sharpd@cumulusnetworks.com>
Signed-off-by: Don Slice <dslice@cumulusnetworks.com>
Signed-off-by: Quentin Young <qlyoung@cumulusnetworks.com>
2018-01-23 19:11:36 +01:00
|
|
|
case PBRMAP_NODE:
|
2017-07-17 14:03:14 +02:00
|
|
|
case OSPF_NODE:
|
|
|
|
case OSPF6_NODE:
|
|
|
|
case LDP_NODE:
|
|
|
|
case LDP_IPV4_NODE:
|
|
|
|
case LDP_IPV6_NODE:
|
|
|
|
case LDP_IPV4_IFACE_NODE:
|
|
|
|
case LDP_IPV6_IFACE_NODE:
|
|
|
|
case LDP_L2VPN_NODE:
|
|
|
|
case LDP_PSEUDOWIRE_NODE:
|
|
|
|
case ISIS_NODE:
|
|
|
|
case KEYCHAIN_NODE:
|
|
|
|
case KEYCHAIN_KEY_NODE:
|
|
|
|
case VTY_NODE:
|
2018-06-27 18:26:06 +02:00
|
|
|
case BFD_NODE:
|
|
|
|
case BFD_PEER_NODE:
|
2017-07-17 14:03:14 +02:00
|
|
|
vty_config_unlock(vty);
|
|
|
|
vty->node = ENABLE_NODE;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
/* Unknown node, we have to ignore it. */
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
vty_prompt(vty);
|
|
|
|
|
|
|
|
/* Set history pointer to the latest one. */
|
|
|
|
vty->hp = vty->hindex;
|
2002-12-13 21:15:29 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Add current command line to the history buffer. */
|
2017-07-17 14:03:14 +02:00
|
|
|
static void vty_hist_add(struct vty *vty)
|
2002-12-13 21:15:29 +01:00
|
|
|
{
|
2017-07-17 14:03:14 +02:00
|
|
|
int index;
|
2002-12-13 21:15:29 +01:00
|
|
|
|
2017-07-17 14:03:14 +02:00
|
|
|
if (vty->length == 0)
|
|
|
|
return;
|
2002-12-13 21:15:29 +01:00
|
|
|
|
2017-07-17 14:03:14 +02:00
|
|
|
index = vty->hindex ? vty->hindex - 1 : VTY_MAXHIST - 1;
|
2002-12-13 21:15:29 +01:00
|
|
|
|
2017-07-17 14:03:14 +02:00
|
|
|
/* Ignore the same string as previous one. */
|
|
|
|
if (vty->hist[index])
|
|
|
|
if (strcmp(vty->buf, vty->hist[index]) == 0) {
|
|
|
|
vty->hp = vty->hindex;
|
|
|
|
return;
|
|
|
|
}
|
2002-12-13 21:15:29 +01:00
|
|
|
|
2017-07-17 14:03:14 +02:00
|
|
|
/* Insert history entry. */
|
|
|
|
if (vty->hist[vty->hindex])
|
|
|
|
XFREE(MTYPE_VTY_HIST, vty->hist[vty->hindex]);
|
|
|
|
vty->hist[vty->hindex] = XSTRDUP(MTYPE_VTY_HIST, vty->buf);
|
2002-12-13 21:15:29 +01:00
|
|
|
|
2017-07-17 14:03:14 +02:00
|
|
|
/* History index rotation. */
|
|
|
|
vty->hindex++;
|
|
|
|
if (vty->hindex == VTY_MAXHIST)
|
|
|
|
vty->hindex = 0;
|
2002-12-13 21:15:29 +01:00
|
|
|
|
2017-07-17 14:03:14 +02:00
|
|
|
vty->hp = vty->hindex;
|
2002-12-13 21:15:29 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/* #define TELNET_OPTION_DEBUG */
|
|
|
|
|
|
|
|
/* Get telnet window size. */
|
2017-07-17 14:03:14 +02:00
|
|
|
static int vty_telnet_option(struct vty *vty, unsigned char *buf, int nbytes)
|
2002-12-13 21:15:29 +01:00
|
|
|
{
|
|
|
|
#ifdef TELNET_OPTION_DEBUG
|
2017-07-17 14:03:14 +02:00
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < nbytes; i++) {
|
|
|
|
switch (buf[i]) {
|
|
|
|
case IAC:
|
|
|
|
vty_out(vty, "IAC ");
|
|
|
|
break;
|
|
|
|
case WILL:
|
|
|
|
vty_out(vty, "WILL ");
|
|
|
|
break;
|
|
|
|
case WONT:
|
|
|
|
vty_out(vty, "WONT ");
|
|
|
|
break;
|
|
|
|
case DO:
|
|
|
|
vty_out(vty, "DO ");
|
|
|
|
break;
|
|
|
|
case DONT:
|
|
|
|
vty_out(vty, "DONT ");
|
|
|
|
break;
|
|
|
|
case SB:
|
|
|
|
vty_out(vty, "SB ");
|
|
|
|
break;
|
|
|
|
case SE:
|
|
|
|
vty_out(vty, "SE ");
|
|
|
|
break;
|
|
|
|
case TELOPT_ECHO:
|
|
|
|
vty_out(vty, "TELOPT_ECHO \n");
|
|
|
|
break;
|
|
|
|
case TELOPT_SGA:
|
|
|
|
vty_out(vty, "TELOPT_SGA \n");
|
|
|
|
break;
|
|
|
|
case TELOPT_NAWS:
|
|
|
|
vty_out(vty, "TELOPT_NAWS \n");
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
vty_out(vty, "%x ", buf[i]);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
vty_out(vty, "\n");
|
2002-12-13 21:15:29 +01:00
|
|
|
|
|
|
|
#endif /* TELNET_OPTION_DEBUG */
|
|
|
|
|
2017-07-17 14:03:14 +02:00
|
|
|
switch (buf[0]) {
|
|
|
|
case SB:
|
|
|
|
vty->sb_len = 0;
|
|
|
|
vty->iac_sb_in_progress = 1;
|
|
|
|
return 0;
|
|
|
|
break;
|
|
|
|
case SE: {
|
|
|
|
if (!vty->iac_sb_in_progress)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
if ((vty->sb_len == 0) || (vty->sb_buf[0] == '\0')) {
|
|
|
|
vty->iac_sb_in_progress = 0;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
switch (vty->sb_buf[0]) {
|
|
|
|
case TELOPT_NAWS:
|
|
|
|
if (vty->sb_len != TELNET_NAWS_SB_LEN)
|
|
|
|
zlog_warn(
|
|
|
|
"RFC 1073 violation detected: telnet NAWS option "
|
|
|
|
"should send %d characters, but we received %lu",
|
|
|
|
TELNET_NAWS_SB_LEN,
|
2018-03-27 21:13:34 +02:00
|
|
|
(unsigned long)vty->sb_len);
|
2017-07-17 14:03:14 +02:00
|
|
|
else if (sizeof(vty->sb_buf) < TELNET_NAWS_SB_LEN)
|
|
|
|
zlog_err(
|
|
|
|
"Bug detected: sizeof(vty->sb_buf) %lu < %d, "
|
|
|
|
"too small to handle the telnet NAWS option",
|
2018-03-27 21:13:34 +02:00
|
|
|
(unsigned long)sizeof(vty->sb_buf),
|
2017-07-17 14:03:14 +02:00
|
|
|
TELNET_NAWS_SB_LEN);
|
|
|
|
else {
|
|
|
|
vty->width = ((vty->sb_buf[1] << 8)
|
|
|
|
| vty->sb_buf[2]);
|
|
|
|
vty->height = ((vty->sb_buf[3] << 8)
|
|
|
|
| vty->sb_buf[4]);
|
2005-02-23 Andrew J. Schorr <ajschorr@alumni.princeton.edu>
* buffer.h: Make the struct buffer and struct buffer_data structures
private by moving them inside buffer.c. Add comments for all
functions. Rename buffer_write as buffer_put (to be more consistent
with the buffer_putc and buffer_putstr functions). Declare a new
buffer_write function that is used to write data to a file descriptor
and/or add it to the buffer queue. Remove unused function
buffer_flush_vty_all. Create a new enum typedef buffer_status_t
to be used as the return code for all buffer_flush* functions
and buffer_write.
* buffer.c: The struct buffer and struct buffer_data declarations
are now private to this file. In conjunction with that, remove
some unnecessary fields: struct buffer (alloc, unused_head,
unused_tail, length), struct buffer_data (prev).
(buffer_data_new) Removed: functionality incorporated into buffer_add.
(buffer_data_free) Removed: use a macro BUFFER_DATA_FREE instead.
(buffer_new) Use calloc instead of malloc + memset(zero).
Supply an appropriate default size if the specified size is 0.
(buffer_free) Eliminate code duplication by calling buffer_reset to
free the contents of the buffer (and remove unused code related
to unused_head).
(buffer_empty,buffer_putc,buffer_putstr) Aesthetic change (make more
compact).
(buffer_reset) Use macro BUFFER_DATA_FREE. No need to set
alloc and length to 0 (these fields have been removed).
(buffer_add) Fix scope to be static. Call XMALLOC directly instead
of calling removed buffer_data_new function. Simplify the logic
(since it's now a singly-linked list instead of doubly-linked).
(buffer_write) Renamed to buffer_put. Change to void, since return
code of 1 was meaningless. No need to adjust length field, since
it has been removed.
(buffer_putw,buffer_flush,buffer_flush_vty_all,buffer_flush_vty)
Remove unused functions.
(buffer_flush_all) Rewrite using buffer_flush_available to eliminate
a possible failure mode if IOV_MAX is less than the number of buffers
on the queue.
(buffer_flush_window) Incorporate logic from buffer_flush_vty.
Log an error message if there is a writev error.
(buffer_flush_available) Be more paranoid: check for case where
buffer is already empty. Use new ERRNO_IO_RETRY macro, and use
new enum for return codes. Simplify deletion logic (since it's
now a singly-linked list).
(buffer_write) New function for use with non-blocking I/O.
* vty.h: Replace the struct vty sb_buffer field with a fixed-size
(5-character) sb_buf field and an sb_len field, since using
a struct buffer was inappropriate for this task. Add some useful
comments about telnet window size negotiation.
* vty.c: Include <arpa/telnet.h> (no longer included by zebra.h).
Remove VTY_OBUF_SIZE (instead use buffer_new default size).
Make telnet_backward_char and telnet_space_char static const.
(vty_out) Replace buffer_write with buffer_put.
(vty_log_out) Check for I/O errors. If fatal, close the vty session.
Consolidate 3 separate writes into a single write call.
(vty_will_echo,vty_command,vty_next_line,vty_previous_line,
vty_end_config,vty_describe_fold,vty_clear_buf,vty_serv_sock_addrinfo,
vty_serv_sock_family,vty_serv_un,vty_use_backup_config,exec_timeout,
vty_config_write,vty_save_cwd) Fix scope to static.
(vty_new) Let buffer_new use its default buffer size.
(vty_write) Fix signature: 2nd arg should be const char *.
Replaced buffer_write with buffer_put.
(vty_telnet_option) Fix minor bug (window height or width greater than
255 was broken). Use sb_buf and sb_len instead of removed sb_buffer
(which was being used improperly).
(vty_read) On error, use ERRNO_IO_RETRY to decide whether it's fatal.
If the error is fatal, call buffer_reset so vty_close does not attempt
to flush the data. Use new sb_buf and sb_len instead of sb_buffer
to store the SB negotiation string.
(vty_flush) When vty->lines is 0, call buffer_flush_available instead
of buffer_flush_window. Look at the return code from buffer_flush
to detect I/O errors (and in that case, log an error message and
close the vty).
(vty_create) Fix scope to static. Initialize sb_len to 0 instead
of creating sb_buffer.
(vty_accept) Set socket nonblocking.
(vtysh_accept) Use new set_nonblocking function instead of calling
fcntl directly.
(vtysh_flush) New function called from vtysh_read (after command
execution) and from vtysh_write. This flushes the buffer
and reacts appropriately to the return code (by closing the vty
or scheduling further flushes).
(vtysh_read) Check whether error is fatal using ERRNO_IO_RETRY.
If not, just try again later. Otherwise, call buffer_reset before
calling vty_close (to avoid trying to flush the buffer in vty_close).
Fix logic to allow case where a command does not arrive atomically
in a single read call by checking for the terminating NUL char.
(vtysh_write) Use new vtysh_flush helper function.
(vty_close) No need to call buffer_empty, just call buffer_flush_all
in any case (it will check whether the buffer is empty).
Do not free sb_buffer (since it has been removed).
(vty_log_fixed) Use writev instead of write.
* zebra.h: Do not include <arpa/telnet.h>, since this is used only
by lib/vty.c.
2005-02-23 16:12:34 +01:00
|
|
|
#ifdef TELNET_OPTION_DEBUG
|
2017-07-17 14:03:14 +02:00
|
|
|
vty_out(vty,
|
|
|
|
"TELNET NAWS window size negotiation completed: "
|
|
|
|
"width %d, height %d\n",
|
|
|
|
vty->width, vty->height);
|
2005-02-23 Andrew J. Schorr <ajschorr@alumni.princeton.edu>
* buffer.h: Make the struct buffer and struct buffer_data structures
private by moving them inside buffer.c. Add comments for all
functions. Rename buffer_write as buffer_put (to be more consistent
with the buffer_putc and buffer_putstr functions). Declare a new
buffer_write function that is used to write data to a file descriptor
and/or add it to the buffer queue. Remove unused function
buffer_flush_vty_all. Create a new enum typedef buffer_status_t
to be used as the return code for all buffer_flush* functions
and buffer_write.
* buffer.c: The struct buffer and struct buffer_data declarations
are now private to this file. In conjunction with that, remove
some unnecessary fields: struct buffer (alloc, unused_head,
unused_tail, length), struct buffer_data (prev).
(buffer_data_new) Removed: functionality incorporated into buffer_add.
(buffer_data_free) Removed: use a macro BUFFER_DATA_FREE instead.
(buffer_new) Use calloc instead of malloc + memset(zero).
Supply an appropriate default size if the specified size is 0.
(buffer_free) Eliminate code duplication by calling buffer_reset to
free the contents of the buffer (and remove unused code related
to unused_head).
(buffer_empty,buffer_putc,buffer_putstr) Aesthetic change (make more
compact).
(buffer_reset) Use macro BUFFER_DATA_FREE. No need to set
alloc and length to 0 (these fields have been removed).
(buffer_add) Fix scope to be static. Call XMALLOC directly instead
of calling removed buffer_data_new function. Simplify the logic
(since it's now a singly-linked list instead of doubly-linked).
(buffer_write) Renamed to buffer_put. Change to void, since return
code of 1 was meaningless. No need to adjust length field, since
it has been removed.
(buffer_putw,buffer_flush,buffer_flush_vty_all,buffer_flush_vty)
Remove unused functions.
(buffer_flush_all) Rewrite using buffer_flush_available to eliminate
a possible failure mode if IOV_MAX is less than the number of buffers
on the queue.
(buffer_flush_window) Incorporate logic from buffer_flush_vty.
Log an error message if there is a writev error.
(buffer_flush_available) Be more paranoid: check for case where
buffer is already empty. Use new ERRNO_IO_RETRY macro, and use
new enum for return codes. Simplify deletion logic (since it's
now a singly-linked list).
(buffer_write) New function for use with non-blocking I/O.
* vty.h: Replace the struct vty sb_buffer field with a fixed-size
(5-character) sb_buf field and an sb_len field, since using
a struct buffer was inappropriate for this task. Add some useful
comments about telnet window size negotiation.
* vty.c: Include <arpa/telnet.h> (no longer included by zebra.h).
Remove VTY_OBUF_SIZE (instead use buffer_new default size).
Make telnet_backward_char and telnet_space_char static const.
(vty_out) Replace buffer_write with buffer_put.
(vty_log_out) Check for I/O errors. If fatal, close the vty session.
Consolidate 3 separate writes into a single write call.
(vty_will_echo,vty_command,vty_next_line,vty_previous_line,
vty_end_config,vty_describe_fold,vty_clear_buf,vty_serv_sock_addrinfo,
vty_serv_sock_family,vty_serv_un,vty_use_backup_config,exec_timeout,
vty_config_write,vty_save_cwd) Fix scope to static.
(vty_new) Let buffer_new use its default buffer size.
(vty_write) Fix signature: 2nd arg should be const char *.
Replaced buffer_write with buffer_put.
(vty_telnet_option) Fix minor bug (window height or width greater than
255 was broken). Use sb_buf and sb_len instead of removed sb_buffer
(which was being used improperly).
(vty_read) On error, use ERRNO_IO_RETRY to decide whether it's fatal.
If the error is fatal, call buffer_reset so vty_close does not attempt
to flush the data. Use new sb_buf and sb_len instead of sb_buffer
to store the SB negotiation string.
(vty_flush) When vty->lines is 0, call buffer_flush_available instead
of buffer_flush_window. Look at the return code from buffer_flush
to detect I/O errors (and in that case, log an error message and
close the vty).
(vty_create) Fix scope to static. Initialize sb_len to 0 instead
of creating sb_buffer.
(vty_accept) Set socket nonblocking.
(vtysh_accept) Use new set_nonblocking function instead of calling
fcntl directly.
(vtysh_flush) New function called from vtysh_read (after command
execution) and from vtysh_write. This flushes the buffer
and reacts appropriately to the return code (by closing the vty
or scheduling further flushes).
(vtysh_read) Check whether error is fatal using ERRNO_IO_RETRY.
If not, just try again later. Otherwise, call buffer_reset before
calling vty_close (to avoid trying to flush the buffer in vty_close).
Fix logic to allow case where a command does not arrive atomically
in a single read call by checking for the terminating NUL char.
(vtysh_write) Use new vtysh_flush helper function.
(vty_close) No need to call buffer_empty, just call buffer_flush_all
in any case (it will check whether the buffer is empty).
Do not free sb_buffer (since it has been removed).
(vty_log_fixed) Use writev instead of write.
* zebra.h: Do not include <arpa/telnet.h>, since this is used only
by lib/vty.c.
2005-02-23 16:12:34 +01:00
|
|
|
#endif
|
2017-07-17 14:03:14 +02:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
vty->iac_sb_in_progress = 0;
|
|
|
|
return 0;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return 1;
|
2002-12-13 21:15:29 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Execute current command line. */
|
2017-07-17 14:03:14 +02:00
|
|
|
static int vty_execute(struct vty *vty)
|
2002-12-13 21:15:29 +01:00
|
|
|
{
|
2017-07-17 14:03:14 +02:00
|
|
|
int ret;
|
2002-12-13 21:15:29 +01:00
|
|
|
|
2017-07-17 14:03:14 +02:00
|
|
|
ret = CMD_SUCCESS;
|
2002-12-13 21:15:29 +01:00
|
|
|
|
2017-07-17 14:03:14 +02:00
|
|
|
switch (vty->node) {
|
|
|
|
case AUTH_NODE:
|
|
|
|
case AUTH_ENABLE_NODE:
|
|
|
|
vty_auth(vty, vty->buf);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
ret = vty_command(vty, vty->buf);
|
|
|
|
if (vty->type == VTY_TERM)
|
|
|
|
vty_hist_add(vty);
|
|
|
|
break;
|
|
|
|
}
|
2002-12-13 21:15:29 +01:00
|
|
|
|
2017-07-17 14:03:14 +02:00
|
|
|
/* Clear command line buffer. */
|
|
|
|
vty->cp = vty->length = 0;
|
|
|
|
vty_clear_buf(vty);
|
2002-12-13 21:15:29 +01:00
|
|
|
|
2017-07-17 14:03:14 +02:00
|
|
|
if (vty->status != VTY_CLOSE)
|
|
|
|
vty_prompt(vty);
|
2002-12-13 21:15:29 +01:00
|
|
|
|
2017-07-17 14:03:14 +02:00
|
|
|
return ret;
|
2002-12-13 21:15:29 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
#define CONTROL(X) ((X) - '@')
|
|
|
|
#define VTY_NORMAL 0
|
|
|
|
#define VTY_PRE_ESCAPE 1
|
|
|
|
#define VTY_ESCAPE 2
|
|
|
|
|
|
|
|
/* Escape character command map. */
|
2017-07-17 14:03:14 +02:00
|
|
|
static void vty_escape_map(unsigned char c, struct vty *vty)
|
|
|
|
{
|
|
|
|
switch (c) {
|
|
|
|
case ('A'):
|
|
|
|
vty_previous_line(vty);
|
|
|
|
break;
|
|
|
|
case ('B'):
|
|
|
|
vty_next_line(vty);
|
|
|
|
break;
|
|
|
|
case ('C'):
|
|
|
|
vty_forward_char(vty);
|
|
|
|
break;
|
|
|
|
case ('D'):
|
|
|
|
vty_backward_char(vty);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Go back to normal mode. */
|
|
|
|
vty->escape = VTY_NORMAL;
|
2002-12-13 21:15:29 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Quit print out to the buffer. */
|
2017-07-17 14:03:14 +02:00
|
|
|
static void vty_buffer_reset(struct vty *vty)
|
2002-12-13 21:15:29 +01:00
|
|
|
{
|
2017-07-17 14:03:14 +02:00
|
|
|
buffer_reset(vty->obuf);
|
2018-06-06 21:09:44 +02:00
|
|
|
buffer_reset(vty->lbuf);
|
2017-07-17 14:03:14 +02:00
|
|
|
vty_prompt(vty);
|
|
|
|
vty_redraw_line(vty);
|
2002-12-13 21:15:29 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Read data via vty socket. */
|
2017-07-17 14:03:14 +02:00
|
|
|
static int vty_read(struct thread *thread)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
int nbytes;
|
|
|
|
unsigned char buf[VTY_READ_BUFSIZ];
|
|
|
|
|
|
|
|
int vty_sock = THREAD_FD(thread);
|
|
|
|
struct vty *vty = THREAD_ARG(thread);
|
|
|
|
vty->t_read = NULL;
|
|
|
|
|
|
|
|
/* Read raw data from socket */
|
|
|
|
if ((nbytes = read(vty->fd, buf, VTY_READ_BUFSIZ)) <= 0) {
|
|
|
|
if (nbytes < 0) {
|
|
|
|
if (ERRNO_IO_RETRY(errno)) {
|
|
|
|
vty_event(VTY_READ, vty_sock, vty);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
vty->monitor = 0; /* disable monitoring to avoid
|
|
|
|
infinite recursion */
|
|
|
|
zlog_warn(
|
|
|
|
"%s: read error on vty client fd %d, closing: %s",
|
|
|
|
__func__, vty->fd, safe_strerror(errno));
|
|
|
|
buffer_reset(vty->obuf);
|
2018-06-06 21:09:44 +02:00
|
|
|
buffer_reset(vty->lbuf);
|
2017-07-17 14:03:14 +02:00
|
|
|
}
|
|
|
|
vty->status = VTY_CLOSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 0; i < nbytes; i++) {
|
|
|
|
if (buf[i] == IAC) {
|
|
|
|
if (!vty->iac) {
|
|
|
|
vty->iac = 1;
|
|
|
|
continue;
|
|
|
|
} else {
|
|
|
|
vty->iac = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (vty->iac_sb_in_progress && !vty->iac) {
|
|
|
|
if (vty->sb_len < sizeof(vty->sb_buf))
|
|
|
|
vty->sb_buf[vty->sb_len] = buf[i];
|
|
|
|
vty->sb_len++;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (vty->iac) {
|
|
|
|
/* In case of telnet command */
|
|
|
|
int ret = 0;
|
|
|
|
ret = vty_telnet_option(vty, buf + i, nbytes - i);
|
|
|
|
vty->iac = 0;
|
|
|
|
i += ret;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (vty->status == VTY_MORE) {
|
|
|
|
switch (buf[i]) {
|
|
|
|
case CONTROL('C'):
|
|
|
|
case 'q':
|
|
|
|
case 'Q':
|
|
|
|
vty_buffer_reset(vty);
|
|
|
|
break;
|
2002-12-13 21:15:29 +01:00
|
|
|
#if 0 /* More line does not work for "show ip bgp". */
|
2016-09-20 01:46:51 +02:00
|
|
|
case '\n':
|
|
|
|
case '\r':
|
|
|
|
vty->status = VTY_MORELINE;
|
|
|
|
break;
|
2002-12-13 21:15:29 +01:00
|
|
|
#endif
|
2017-07-17 14:03:14 +02:00
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Escape character. */
|
|
|
|
if (vty->escape == VTY_ESCAPE) {
|
|
|
|
vty_escape_map(buf[i], vty);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Pre-escape status. */
|
|
|
|
if (vty->escape == VTY_PRE_ESCAPE) {
|
|
|
|
switch (buf[i]) {
|
|
|
|
case '[':
|
|
|
|
vty->escape = VTY_ESCAPE;
|
|
|
|
break;
|
|
|
|
case 'b':
|
|
|
|
vty_backward_word(vty);
|
|
|
|
vty->escape = VTY_NORMAL;
|
|
|
|
break;
|
|
|
|
case 'f':
|
|
|
|
vty_forward_word(vty);
|
|
|
|
vty->escape = VTY_NORMAL;
|
|
|
|
break;
|
|
|
|
case 'd':
|
|
|
|
vty_forward_kill_word(vty);
|
|
|
|
vty->escape = VTY_NORMAL;
|
|
|
|
break;
|
|
|
|
case CONTROL('H'):
|
|
|
|
case 0x7f:
|
|
|
|
vty_backward_kill_word(vty);
|
|
|
|
vty->escape = VTY_NORMAL;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
vty->escape = VTY_NORMAL;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (buf[i]) {
|
|
|
|
case CONTROL('A'):
|
|
|
|
vty_beginning_of_line(vty);
|
|
|
|
break;
|
|
|
|
case CONTROL('B'):
|
|
|
|
vty_backward_char(vty);
|
|
|
|
break;
|
|
|
|
case CONTROL('C'):
|
|
|
|
vty_stop_input(vty);
|
|
|
|
break;
|
|
|
|
case CONTROL('D'):
|
|
|
|
vty_delete_char(vty);
|
|
|
|
break;
|
|
|
|
case CONTROL('E'):
|
|
|
|
vty_end_of_line(vty);
|
|
|
|
break;
|
|
|
|
case CONTROL('F'):
|
|
|
|
vty_forward_char(vty);
|
|
|
|
break;
|
|
|
|
case CONTROL('H'):
|
|
|
|
case 0x7f:
|
|
|
|
vty_delete_backward_char(vty);
|
|
|
|
break;
|
|
|
|
case CONTROL('K'):
|
|
|
|
vty_kill_line(vty);
|
|
|
|
break;
|
|
|
|
case CONTROL('N'):
|
|
|
|
vty_next_line(vty);
|
|
|
|
break;
|
|
|
|
case CONTROL('P'):
|
|
|
|
vty_previous_line(vty);
|
|
|
|
break;
|
|
|
|
case CONTROL('T'):
|
|
|
|
vty_transpose_chars(vty);
|
|
|
|
break;
|
|
|
|
case CONTROL('U'):
|
|
|
|
vty_kill_line_from_beginning(vty);
|
|
|
|
break;
|
|
|
|
case CONTROL('W'):
|
|
|
|
vty_backward_kill_word(vty);
|
|
|
|
break;
|
|
|
|
case CONTROL('Z'):
|
|
|
|
vty_end_config(vty);
|
|
|
|
break;
|
|
|
|
case '\n':
|
|
|
|
case '\r':
|
|
|
|
vty_out(vty, "\n");
|
|
|
|
vty_execute(vty);
|
|
|
|
break;
|
|
|
|
case '\t':
|
|
|
|
vty_complete_command(vty);
|
|
|
|
break;
|
|
|
|
case '?':
|
|
|
|
if (vty->node == AUTH_NODE
|
|
|
|
|| vty->node == AUTH_ENABLE_NODE)
|
|
|
|
vty_self_insert(vty, buf[i]);
|
|
|
|
else
|
|
|
|
vty_describe_command(vty);
|
|
|
|
break;
|
|
|
|
case '\033':
|
|
|
|
if (i + 1 < nbytes && buf[i + 1] == '[') {
|
|
|
|
vty->escape = VTY_ESCAPE;
|
|
|
|
i++;
|
|
|
|
} else
|
|
|
|
vty->escape = VTY_PRE_ESCAPE;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
if (buf[i] > 31 && buf[i] < 127)
|
|
|
|
vty_self_insert(vty, buf[i]);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Check status. */
|
|
|
|
if (vty->status == VTY_CLOSE)
|
|
|
|
vty_close(vty);
|
|
|
|
else {
|
|
|
|
vty_event(VTY_WRITE, vty->wfd, vty);
|
|
|
|
vty_event(VTY_READ, vty_sock, vty);
|
|
|
|
}
|
|
|
|
return 0;
|
2002-12-13 21:15:29 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Flush buffer to the vty. */
|
2017-07-17 14:03:14 +02:00
|
|
|
static int vty_flush(struct thread *thread)
|
|
|
|
{
|
|
|
|
int erase;
|
|
|
|
buffer_status_t flushrc;
|
|
|
|
int vty_sock = THREAD_FD(thread);
|
|
|
|
struct vty *vty = THREAD_ARG(thread);
|
|
|
|
|
|
|
|
vty->t_write = NULL;
|
|
|
|
|
|
|
|
/* Tempolary disable read thread. */
|
|
|
|
if ((vty->lines == 0) && vty->t_read) {
|
|
|
|
thread_cancel(vty->t_read);
|
|
|
|
vty->t_read = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Function execution continue. */
|
|
|
|
erase = ((vty->status == VTY_MORE || vty->status == VTY_MORELINE));
|
|
|
|
|
|
|
|
/* N.B. if width is 0, that means we don't know the window size. */
|
|
|
|
if ((vty->lines == 0) || (vty->width == 0) || (vty->height == 0))
|
|
|
|
flushrc = buffer_flush_available(vty->obuf, vty_sock);
|
|
|
|
else if (vty->status == VTY_MORELINE)
|
|
|
|
flushrc = buffer_flush_window(vty->obuf, vty_sock, vty->width,
|
|
|
|
1, erase, 0);
|
|
|
|
else
|
|
|
|
flushrc = buffer_flush_window(
|
|
|
|
vty->obuf, vty_sock, vty->width,
|
|
|
|
vty->lines >= 0 ? vty->lines : vty->height, erase, 0);
|
|
|
|
switch (flushrc) {
|
|
|
|
case BUFFER_ERROR:
|
|
|
|
vty->monitor =
|
|
|
|
0; /* disable monitoring to avoid infinite recursion */
|
|
|
|
zlog_warn("buffer_flush failed on vty client fd %d, closing",
|
|
|
|
vty->fd);
|
2018-06-06 21:09:44 +02:00
|
|
|
buffer_reset(vty->lbuf);
|
2017-07-17 14:03:14 +02:00
|
|
|
buffer_reset(vty->obuf);
|
|
|
|
vty_close(vty);
|
|
|
|
return 0;
|
|
|
|
case BUFFER_EMPTY:
|
|
|
|
if (vty->status == VTY_CLOSE)
|
|
|
|
vty_close(vty);
|
|
|
|
else {
|
|
|
|
vty->status = VTY_NORMAL;
|
|
|
|
if (vty->lines == 0)
|
|
|
|
vty_event(VTY_READ, vty_sock, vty);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case BUFFER_PENDING:
|
|
|
|
/* There is more data waiting to be written. */
|
|
|
|
vty->status = VTY_MORE;
|
|
|
|
if (vty->lines == 0)
|
|
|
|
vty_event(VTY_WRITE, vty_sock, vty);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
2002-12-13 21:15:29 +01:00
|
|
|
}
|
|
|
|
|
2018-05-15 00:13:03 +02:00
|
|
|
/* Allocate new vty struct. */
|
|
|
|
struct vty *vty_new()
|
|
|
|
{
|
|
|
|
struct vty *new = XCALLOC(MTYPE_VTY, sizeof(struct vty));
|
|
|
|
|
|
|
|
new->fd = new->wfd = -1;
|
2018-06-01 22:13:18 +02:00
|
|
|
new->of = stdout;
|
2018-06-06 21:09:44 +02:00
|
|
|
new->lbuf = buffer_new(0);
|
2018-05-15 00:13:03 +02:00
|
|
|
new->obuf = buffer_new(0); /* Use default buffer size. */
|
|
|
|
new->buf = XCALLOC(MTYPE_VTY, VTY_BUFSIZ);
|
|
|
|
new->error_buf = XCALLOC(MTYPE_VTY, VTY_BUFSIZ);
|
|
|
|
new->max = VTY_BUFSIZ;
|
|
|
|
|
|
|
|
return new;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-05-30 16:33:45 +02:00
|
|
|
/* allocate and initialise vty */
|
2017-07-17 14:03:14 +02:00
|
|
|
static struct vty *vty_new_init(int vty_sock)
|
|
|
|
{
|
|
|
|
struct vty *vty;
|
|
|
|
|
|
|
|
vty = vty_new();
|
|
|
|
vty->fd = vty_sock;
|
|
|
|
vty->wfd = vty_sock;
|
|
|
|
vty->type = VTY_TERM;
|
|
|
|
vty->node = AUTH_NODE;
|
|
|
|
vty->fail = 0;
|
|
|
|
vty->cp = 0;
|
|
|
|
vty_clear_buf(vty);
|
|
|
|
vty->length = 0;
|
|
|
|
memset(vty->hist, 0, sizeof(vty->hist));
|
|
|
|
vty->hp = 0;
|
|
|
|
vty->hindex = 0;
|
|
|
|
vector_set_index(vtyvec, vty_sock, vty);
|
|
|
|
vty->status = VTY_NORMAL;
|
|
|
|
vty->lines = -1;
|
|
|
|
vty->iac = 0;
|
|
|
|
vty->iac_sb_in_progress = 0;
|
|
|
|
vty->sb_len = 0;
|
|
|
|
|
|
|
|
return vty;
|
2013-05-30 16:33:45 +02:00
|
|
|
}
|
|
|
|
|
2002-12-13 21:15:29 +01:00
|
|
|
/* Create new vty structure. */
|
2017-07-17 14:03:14 +02:00
|
|
|
static struct vty *vty_create(int vty_sock, union sockunion *su)
|
|
|
|
{
|
|
|
|
char buf[SU_ADDRSTRLEN];
|
|
|
|
struct vty *vty;
|
|
|
|
|
|
|
|
sockunion2str(su, buf, SU_ADDRSTRLEN);
|
|
|
|
|
|
|
|
/* Allocate new vty structure and set up default values. */
|
|
|
|
vty = vty_new_init(vty_sock);
|
|
|
|
|
|
|
|
/* configurable parameters not part of basic init */
|
|
|
|
vty->v_timeout = vty_timeout_val;
|
|
|
|
strcpy(vty->address, buf);
|
|
|
|
if (no_password_check) {
|
|
|
|
if (host.advanced)
|
|
|
|
vty->node = ENABLE_NODE;
|
|
|
|
else
|
|
|
|
vty->node = VIEW_NODE;
|
|
|
|
}
|
|
|
|
if (host.lines >= 0)
|
|
|
|
vty->lines = host.lines;
|
|
|
|
|
|
|
|
if (!no_password_check) {
|
|
|
|
/* Vty is not available if password isn't set. */
|
|
|
|
if (host.password == NULL && host.password_encrypt == NULL) {
|
|
|
|
vty_out(vty, "Vty password is not set.\n");
|
|
|
|
vty->status = VTY_CLOSE;
|
|
|
|
vty_close(vty);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Say hello to the world. */
|
|
|
|
vty_hello(vty);
|
|
|
|
if (!no_password_check)
|
|
|
|
vty_out(vty, "\nUser Access Verification\n\n");
|
|
|
|
|
|
|
|
/* Setting up terminal. */
|
|
|
|
vty_will_echo(vty);
|
|
|
|
vty_will_suppress_go_ahead(vty);
|
|
|
|
|
|
|
|
vty_dont_linemode(vty);
|
|
|
|
vty_do_window_size(vty);
|
|
|
|
/* vty_dont_lflow_ahead (vty); */
|
|
|
|
|
|
|
|
vty_prompt(vty);
|
|
|
|
|
|
|
|
/* Add read/write thread. */
|
|
|
|
vty_event(VTY_WRITE, vty_sock, vty);
|
|
|
|
vty_event(VTY_READ, vty_sock, vty);
|
|
|
|
|
|
|
|
return vty;
|
2002-12-13 21:15:29 +01:00
|
|
|
}
|
|
|
|
|
2013-05-30 16:33:45 +02:00
|
|
|
/* create vty for stdio */
|
2015-05-05 11:04:46 +02:00
|
|
|
static struct termios stdio_orig_termios;
|
|
|
|
static struct vty *stdio_vty = NULL;
|
2017-06-01 16:02:23 +02:00
|
|
|
static bool stdio_termios = false;
|
|
|
|
static void (*stdio_vty_atclose)(int isexit);
|
2015-05-05 11:04:46 +02:00
|
|
|
|
2017-06-01 16:02:23 +02:00
|
|
|
static void vty_stdio_reset(int isexit)
|
2015-05-05 11:04:46 +02:00
|
|
|
{
|
2017-07-17 14:03:14 +02:00
|
|
|
if (stdio_vty) {
|
2017-06-01 16:02:23 +02:00
|
|
|
if (stdio_termios)
|
|
|
|
tcsetattr(0, TCSANOW, &stdio_orig_termios);
|
|
|
|
stdio_termios = false;
|
|
|
|
|
2017-07-17 14:03:14 +02:00
|
|
|
stdio_vty = NULL;
|
2015-05-12 21:56:18 +02:00
|
|
|
|
2017-07-17 14:03:14 +02:00
|
|
|
if (stdio_vty_atclose)
|
2017-06-01 16:02:23 +02:00
|
|
|
stdio_vty_atclose(isexit);
|
2017-07-17 14:03:14 +02:00
|
|
|
stdio_vty_atclose = NULL;
|
|
|
|
}
|
2015-05-05 11:04:46 +02:00
|
|
|
}
|
|
|
|
|
2017-06-01 16:02:23 +02:00
|
|
|
static void vty_stdio_atexit(void)
|
|
|
|
{
|
|
|
|
vty_stdio_reset(1);
|
|
|
|
}
|
|
|
|
|
|
|
|
void vty_stdio_suspend(void)
|
|
|
|
{
|
|
|
|
if (!stdio_vty)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (stdio_vty->t_write)
|
|
|
|
thread_cancel(stdio_vty->t_write);
|
|
|
|
if (stdio_vty->t_read)
|
|
|
|
thread_cancel(stdio_vty->t_read);
|
|
|
|
if (stdio_vty->t_timeout)
|
|
|
|
thread_cancel(stdio_vty->t_timeout);
|
|
|
|
|
|
|
|
if (stdio_termios)
|
|
|
|
tcsetattr(0, TCSANOW, &stdio_orig_termios);
|
|
|
|
stdio_termios = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
void vty_stdio_resume(void)
|
|
|
|
{
|
|
|
|
if (!stdio_vty)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (!tcgetattr(0, &stdio_orig_termios)) {
|
|
|
|
struct termios termios;
|
|
|
|
|
|
|
|
termios = stdio_orig_termios;
|
|
|
|
termios.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR
|
|
|
|
| IGNCR | ICRNL | IXON);
|
|
|
|
termios.c_oflag &= ~OPOST;
|
|
|
|
termios.c_lflag &= ~(ECHO | ECHONL | ICANON | IEXTEN);
|
|
|
|
termios.c_cflag &= ~(CSIZE | PARENB);
|
|
|
|
termios.c_cflag |= CS8;
|
|
|
|
tcsetattr(0, TCSANOW, &termios);
|
|
|
|
stdio_termios = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
vty_prompt(stdio_vty);
|
|
|
|
|
|
|
|
/* Add read/write thread. */
|
|
|
|
vty_event(VTY_WRITE, 1, stdio_vty);
|
|
|
|
vty_event(VTY_READ, 0, stdio_vty);
|
|
|
|
}
|
|
|
|
|
|
|
|
void vty_stdio_close(void)
|
|
|
|
{
|
|
|
|
if (!stdio_vty)
|
|
|
|
return;
|
|
|
|
vty_close(stdio_vty);
|
|
|
|
}
|
|
|
|
|
|
|
|
struct vty *vty_stdio(void (*atclose)(int isexit))
|
2013-05-30 16:33:45 +02:00
|
|
|
{
|
2017-07-17 14:03:14 +02:00
|
|
|
struct vty *vty;
|
2013-05-30 16:33:45 +02:00
|
|
|
|
2017-07-17 14:03:14 +02:00
|
|
|
/* refuse creating two vtys on stdio */
|
|
|
|
if (stdio_vty)
|
|
|
|
return NULL;
|
2015-05-05 11:04:46 +02:00
|
|
|
|
2017-07-17 14:03:14 +02:00
|
|
|
vty = stdio_vty = vty_new_init(0);
|
|
|
|
stdio_vty_atclose = atclose;
|
|
|
|
vty->wfd = 1;
|
2013-05-30 16:33:45 +02:00
|
|
|
|
2017-07-17 14:03:14 +02:00
|
|
|
/* always have stdio vty in a known _unchangeable_ state, don't want
|
|
|
|
* config
|
|
|
|
* to have any effect here to make sure scripting this works as intended
|
|
|
|
*/
|
|
|
|
vty->node = ENABLE_NODE;
|
|
|
|
vty->v_timeout = 0;
|
|
|
|
strcpy(vty->address, "console");
|
2013-05-30 16:33:45 +02:00
|
|
|
|
2017-06-01 16:02:23 +02:00
|
|
|
vty_stdio_resume();
|
2017-07-17 14:03:14 +02:00
|
|
|
return vty;
|
2013-05-30 16:33:45 +02:00
|
|
|
}
|
|
|
|
|
2002-12-13 21:15:29 +01:00
|
|
|
/* Accept connection from the network. */
|
2017-07-17 14:03:14 +02:00
|
|
|
static int vty_accept(struct thread *thread)
|
|
|
|
{
|
|
|
|
int vty_sock;
|
|
|
|
union sockunion su;
|
|
|
|
int ret;
|
|
|
|
unsigned int on;
|
|
|
|
int accept_sock;
|
|
|
|
struct prefix p;
|
|
|
|
struct access_list *acl = NULL;
|
|
|
|
char buf[SU_ADDRSTRLEN];
|
|
|
|
|
|
|
|
accept_sock = THREAD_FD(thread);
|
|
|
|
|
|
|
|
/* We continue hearing vty socket. */
|
|
|
|
vty_event(VTY_SERV, accept_sock, NULL);
|
|
|
|
|
|
|
|
memset(&su, 0, sizeof(union sockunion));
|
|
|
|
|
|
|
|
/* We can handle IPv4 or IPv6 socket. */
|
|
|
|
vty_sock = sockunion_accept(accept_sock, &su);
|
|
|
|
if (vty_sock < 0) {
|
|
|
|
zlog_warn("can't accept vty socket : %s", safe_strerror(errno));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
set_nonblocking(vty_sock);
|
|
|
|
set_cloexec(vty_sock);
|
|
|
|
|
|
|
|
sockunion2hostprefix(&su, &p);
|
|
|
|
|
|
|
|
/* VTY's accesslist apply. */
|
|
|
|
if (p.family == AF_INET && vty_accesslist_name) {
|
|
|
|
if ((acl = access_list_lookup(AFI_IP, vty_accesslist_name))
|
|
|
|
&& (access_list_apply(acl, &p) == FILTER_DENY)) {
|
|
|
|
zlog_info("Vty connection refused from %s",
|
|
|
|
sockunion2str(&su, buf, SU_ADDRSTRLEN));
|
|
|
|
close(vty_sock);
|
|
|
|
|
|
|
|
/* continue accepting connections */
|
|
|
|
vty_event(VTY_SERV, accept_sock, NULL);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* VTY's ipv6 accesslist apply. */
|
|
|
|
if (p.family == AF_INET6 && vty_ipv6_accesslist_name) {
|
|
|
|
if ((acl = access_list_lookup(AFI_IP6,
|
|
|
|
vty_ipv6_accesslist_name))
|
|
|
|
&& (access_list_apply(acl, &p) == FILTER_DENY)) {
|
|
|
|
zlog_info("Vty connection refused from %s",
|
|
|
|
sockunion2str(&su, buf, SU_ADDRSTRLEN));
|
|
|
|
close(vty_sock);
|
|
|
|
|
|
|
|
/* continue accepting connections */
|
|
|
|
vty_event(VTY_SERV, accept_sock, NULL);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
on = 1;
|
|
|
|
ret = setsockopt(vty_sock, IPPROTO_TCP, TCP_NODELAY, (char *)&on,
|
|
|
|
sizeof(on));
|
|
|
|
if (ret < 0)
|
|
|
|
zlog_info("can't set sockopt to vty_sock : %s",
|
|
|
|
safe_strerror(errno));
|
|
|
|
|
|
|
|
zlog_info("Vty connection from %s",
|
|
|
|
sockunion2str(&su, buf, SU_ADDRSTRLEN));
|
|
|
|
|
|
|
|
vty_create(vty_sock, &su);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void vty_serv_sock_addrinfo(const char *hostname, unsigned short port)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
struct addrinfo req;
|
|
|
|
struct addrinfo *ainfo;
|
|
|
|
struct addrinfo *ainfo_save;
|
|
|
|
int sock;
|
|
|
|
char port_str[BUFSIZ];
|
|
|
|
|
|
|
|
memset(&req, 0, sizeof(struct addrinfo));
|
|
|
|
req.ai_flags = AI_PASSIVE;
|
|
|
|
req.ai_family = AF_UNSPEC;
|
|
|
|
req.ai_socktype = SOCK_STREAM;
|
|
|
|
sprintf(port_str, "%d", port);
|
|
|
|
port_str[sizeof(port_str) - 1] = '\0';
|
|
|
|
|
|
|
|
ret = getaddrinfo(hostname, port_str, &req, &ainfo);
|
|
|
|
|
|
|
|
if (ret != 0) {
|
2018-06-14 15:13:18 +02:00
|
|
|
zlog_ferr(LIB_ERR_SYSTEM_CALL,
|
|
|
|
"getaddrinfo failed: %s", gai_strerror(ret));
|
2017-07-17 14:03:14 +02:00
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
|
|
|
|
ainfo_save = ainfo;
|
|
|
|
|
|
|
|
do {
|
|
|
|
if (ainfo->ai_family != AF_INET && ainfo->ai_family != AF_INET6)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
sock = socket(ainfo->ai_family, ainfo->ai_socktype,
|
|
|
|
ainfo->ai_protocol);
|
|
|
|
if (sock < 0)
|
|
|
|
continue;
|
2002-12-13 21:15:29 +01:00
|
|
|
|
2017-07-17 14:03:14 +02:00
|
|
|
sockopt_v6only(ainfo->ai_family, sock);
|
|
|
|
sockopt_reuseaddr(sock);
|
|
|
|
sockopt_reuseport(sock);
|
|
|
|
set_cloexec(sock);
|
|
|
|
|
|
|
|
ret = bind(sock, ainfo->ai_addr, ainfo->ai_addrlen);
|
|
|
|
if (ret < 0) {
|
|
|
|
close(sock); /* Avoid sd leak. */
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = listen(sock, 3);
|
|
|
|
if (ret < 0) {
|
|
|
|
close(sock); /* Avoid sd leak. */
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
vty_event(VTY_SERV, sock, NULL);
|
|
|
|
} while ((ainfo = ainfo->ai_next) != NULL);
|
|
|
|
|
|
|
|
freeaddrinfo(ainfo_save);
|
2002-12-13 21:15:29 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef VTYSH
|
|
|
|
/* For sockaddr_un. */
|
|
|
|
#include <sys/un.h>
|
|
|
|
|
|
|
|
/* VTY shell UNIX domain socket. */
|
2017-07-17 14:03:14 +02:00
|
|
|
static void vty_serv_un(const char *path)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
int sock, len;
|
|
|
|
struct sockaddr_un serv;
|
|
|
|
mode_t old_mask;
|
|
|
|
struct zprivs_ids_t ids;
|
|
|
|
|
|
|
|
/* First of all, unlink existing socket */
|
|
|
|
unlink(path);
|
|
|
|
|
|
|
|
/* Set umask */
|
|
|
|
old_mask = umask(0007);
|
|
|
|
|
|
|
|
/* Make UNIX domain socket. */
|
|
|
|
sock = socket(AF_UNIX, SOCK_STREAM, 0);
|
|
|
|
if (sock < 0) {
|
2018-06-14 15:13:18 +02:00
|
|
|
zlog_ferr(LIB_ERR_SOCKET,
|
|
|
|
"Cannot create unix stream socket: %s",
|
2017-07-17 14:03:14 +02:00
|
|
|
safe_strerror(errno));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Make server socket. */
|
|
|
|
memset(&serv, 0, sizeof(struct sockaddr_un));
|
|
|
|
serv.sun_family = AF_UNIX;
|
|
|
|
strlcpy(serv.sun_path, path, sizeof(serv.sun_path));
|
[autoconf] bugs 162,303,178: Fix 'present but can not be compiled' warnings
2007-05-09 Paul Jakma <paul.jakma@sun.com>
* configure.ac: sys/conf.h depends on sys/param.h, at least on
FBSD 6.2.
(bug #363) Should check for in_pktinfo for IRDP
2006-05-27 Paul Jakma <paul.jakma@sun.com>
* configure.ac: General cleanup of header and type checks, introducing
an internal define, QUAGGA_INCLUDES, to build up a list of
stuff to include so as to avoid 'present but cant be compiled'
warnings.
Misc additional checks of things missing according to autoscan.
Add LIBM, for bgpd's use of libm, so as to avoid burdening
LIBS, and all the binaries, with libm linkage.
Remove the bad practice of using m4 changequote(), just
quote the []'s in the case statements properly.
This should fix bugs 162, 303 and 178.
* */*.{c,h}: Update all HAVE_* to the standard autoconf namespaced
HAVE_* defines. I.e. HAVE_SA_LEN -> HAVE_STRUCT_SOCKADDR_SA_LEN,
* bgpd/Makefile.am: Add LIBM to bgpd's LDADD, for pow().
2007-05-10 04:38:51 +02:00
|
|
|
#ifdef HAVE_STRUCT_SOCKADDR_UN_SUN_LEN
|
2017-07-17 14:03:14 +02:00
|
|
|
len = serv.sun_len = SUN_LEN(&serv);
|
2002-12-13 21:15:29 +01:00
|
|
|
#else
|
2017-07-17 14:03:14 +02:00
|
|
|
len = sizeof(serv.sun_family) + strlen(serv.sun_path);
|
[autoconf] bugs 162,303,178: Fix 'present but can not be compiled' warnings
2007-05-09 Paul Jakma <paul.jakma@sun.com>
* configure.ac: sys/conf.h depends on sys/param.h, at least on
FBSD 6.2.
(bug #363) Should check for in_pktinfo for IRDP
2006-05-27 Paul Jakma <paul.jakma@sun.com>
* configure.ac: General cleanup of header and type checks, introducing
an internal define, QUAGGA_INCLUDES, to build up a list of
stuff to include so as to avoid 'present but cant be compiled'
warnings.
Misc additional checks of things missing according to autoscan.
Add LIBM, for bgpd's use of libm, so as to avoid burdening
LIBS, and all the binaries, with libm linkage.
Remove the bad practice of using m4 changequote(), just
quote the []'s in the case statements properly.
This should fix bugs 162, 303 and 178.
* */*.{c,h}: Update all HAVE_* to the standard autoconf namespaced
HAVE_* defines. I.e. HAVE_SA_LEN -> HAVE_STRUCT_SOCKADDR_SA_LEN,
* bgpd/Makefile.am: Add LIBM to bgpd's LDADD, for pow().
2007-05-10 04:38:51 +02:00
|
|
|
#endif /* HAVE_STRUCT_SOCKADDR_UN_SUN_LEN */
|
2002-12-13 21:15:29 +01:00
|
|
|
|
2017-07-17 14:03:14 +02:00
|
|
|
set_cloexec(sock);
|
2016-11-08 20:46:05 +01:00
|
|
|
|
2017-07-17 14:03:14 +02:00
|
|
|
ret = bind(sock, (struct sockaddr *)&serv, len);
|
|
|
|
if (ret < 0) {
|
2018-06-14 15:13:18 +02:00
|
|
|
zlog_ferr(LIB_ERR_SOCKET,
|
|
|
|
"Cannot bind path %s: %s",
|
|
|
|
path, safe_strerror(errno));
|
2017-07-17 14:03:14 +02:00
|
|
|
close(sock); /* Avoid sd leak. */
|
|
|
|
return;
|
|
|
|
}
|
2002-12-13 21:15:29 +01:00
|
|
|
|
2017-07-17 14:03:14 +02:00
|
|
|
ret = listen(sock, 5);
|
|
|
|
if (ret < 0) {
|
2018-06-14 15:13:18 +02:00
|
|
|
zlog_ferr(LIB_ERR_SOCKET,
|
|
|
|
"listen(fd %d) failed: %s", sock,
|
|
|
|
safe_strerror(errno));
|
2017-07-17 14:03:14 +02:00
|
|
|
close(sock); /* Avoid sd leak. */
|
|
|
|
return;
|
|
|
|
}
|
2002-12-13 21:15:29 +01:00
|
|
|
|
2017-07-17 14:03:14 +02:00
|
|
|
umask(old_mask);
|
2002-12-13 21:15:29 +01:00
|
|
|
|
2017-07-17 14:03:14 +02:00
|
|
|
zprivs_get_ids(&ids);
|
2016-09-20 01:46:51 +02:00
|
|
|
|
2017-07-17 14:03:14 +02:00
|
|
|
/* Hack: ids.gid_vty is actually a uint, but we stored -1 in it
|
|
|
|
earlier for the case when we don't need to chown the file
|
|
|
|
type casting it here to make a compare */
|
|
|
|
if ((int)ids.gid_vty > 0) {
|
|
|
|
/* set group of socket */
|
|
|
|
if (chown(path, -1, ids.gid_vty)) {
|
2018-06-14 15:13:18 +02:00
|
|
|
zlog_ferr(LIB_ERR_SYSTEM_CALL,
|
|
|
|
"vty_serv_un: could chown socket, %s",
|
|
|
|
safe_strerror(errno));
|
2017-07-17 14:03:14 +02:00
|
|
|
}
|
|
|
|
}
|
2003-06-04 15:59:38 +02:00
|
|
|
|
2017-07-17 14:03:14 +02:00
|
|
|
vty_event(VTYSH_SERV, sock, NULL);
|
2002-12-13 21:15:29 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/* #define VTYSH_DEBUG 1 */
|
|
|
|
|
2017-07-17 14:03:14 +02:00
|
|
|
static int vtysh_accept(struct thread *thread)
|
2002-12-13 21:15:29 +01:00
|
|
|
{
|
2017-07-17 14:03:14 +02:00
|
|
|
int accept_sock;
|
|
|
|
int sock;
|
|
|
|
int client_len;
|
|
|
|
struct sockaddr_un client;
|
|
|
|
struct vty *vty;
|
2016-09-20 01:46:51 +02:00
|
|
|
|
2017-07-17 14:03:14 +02:00
|
|
|
accept_sock = THREAD_FD(thread);
|
2002-12-13 21:15:29 +01:00
|
|
|
|
2017-07-17 14:03:14 +02:00
|
|
|
vty_event(VTYSH_SERV, accept_sock, NULL);
|
2002-12-13 21:15:29 +01:00
|
|
|
|
2017-07-17 14:03:14 +02:00
|
|
|
memset(&client, 0, sizeof(struct sockaddr_un));
|
|
|
|
client_len = sizeof(struct sockaddr_un);
|
2002-12-13 21:15:29 +01:00
|
|
|
|
2017-07-17 14:03:14 +02:00
|
|
|
sock = accept(accept_sock, (struct sockaddr *)&client,
|
|
|
|
(socklen_t *)&client_len);
|
2002-12-13 21:15:29 +01:00
|
|
|
|
2017-07-17 14:03:14 +02:00
|
|
|
if (sock < 0) {
|
|
|
|
zlog_warn("can't accept vty socket : %s", safe_strerror(errno));
|
|
|
|
return -1;
|
|
|
|
}
|
2002-12-13 21:15:29 +01:00
|
|
|
|
2017-07-17 14:03:14 +02:00
|
|
|
if (set_nonblocking(sock) < 0) {
|
|
|
|
zlog_warn(
|
|
|
|
"vtysh_accept: could not set vty socket %d to non-blocking,"
|
|
|
|
" %s, closing",
|
|
|
|
sock, safe_strerror(errno));
|
|
|
|
close(sock);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
set_cloexec(sock);
|
2016-11-08 20:46:05 +01:00
|
|
|
|
2002-12-13 21:15:29 +01:00
|
|
|
#ifdef VTYSH_DEBUG
|
2017-07-17 14:03:14 +02:00
|
|
|
printf("VTY shell accept\n");
|
2002-12-13 21:15:29 +01:00
|
|
|
#endif /* VTYSH_DEBUG */
|
|
|
|
|
2017-07-17 14:03:14 +02:00
|
|
|
vty = vty_new();
|
|
|
|
vty->fd = sock;
|
|
|
|
vty->wfd = sock;
|
|
|
|
vty->type = VTY_SHELL_SERV;
|
|
|
|
vty->node = VIEW_NODE;
|
|
|
|
|
|
|
|
vty_event(VTYSH_READ, sock, vty);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int vtysh_flush(struct vty *vty)
|
|
|
|
{
|
|
|
|
switch (buffer_flush_available(vty->obuf, vty->wfd)) {
|
|
|
|
case BUFFER_PENDING:
|
|
|
|
vty_event(VTYSH_WRITE, vty->wfd, vty);
|
|
|
|
break;
|
|
|
|
case BUFFER_ERROR:
|
|
|
|
vty->monitor =
|
|
|
|
0; /* disable monitoring to avoid infinite recursion */
|
|
|
|
zlog_warn("%s: write error to fd %d, closing", __func__,
|
|
|
|
vty->fd);
|
2018-06-06 21:09:44 +02:00
|
|
|
buffer_reset(vty->lbuf);
|
2017-07-17 14:03:14 +02:00
|
|
|
buffer_reset(vty->obuf);
|
|
|
|
vty_close(vty);
|
|
|
|
return -1;
|
|
|
|
break;
|
|
|
|
case BUFFER_EMPTY:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int vtysh_read(struct thread *thread)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
int sock;
|
|
|
|
int nbytes;
|
|
|
|
struct vty *vty;
|
|
|
|
unsigned char buf[VTY_READ_BUFSIZ];
|
|
|
|
unsigned char *p;
|
2018-03-27 21:13:34 +02:00
|
|
|
uint8_t header[4] = {0, 0, 0, 0};
|
2017-07-17 14:03:14 +02:00
|
|
|
|
|
|
|
sock = THREAD_FD(thread);
|
|
|
|
vty = THREAD_ARG(thread);
|
|
|
|
vty->t_read = NULL;
|
|
|
|
|
|
|
|
if ((nbytes = read(sock, buf, VTY_READ_BUFSIZ)) <= 0) {
|
|
|
|
if (nbytes < 0) {
|
|
|
|
if (ERRNO_IO_RETRY(errno)) {
|
|
|
|
vty_event(VTYSH_READ, sock, vty);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
vty->monitor = 0; /* disable monitoring to avoid
|
|
|
|
infinite recursion */
|
|
|
|
zlog_warn(
|
|
|
|
"%s: read failed on vtysh client fd %d, closing: %s",
|
|
|
|
__func__, sock, safe_strerror(errno));
|
|
|
|
}
|
2018-06-06 21:09:44 +02:00
|
|
|
buffer_reset(vty->lbuf);
|
2017-07-17 14:03:14 +02:00
|
|
|
buffer_reset(vty->obuf);
|
|
|
|
vty_close(vty);
|
2002-12-13 21:15:29 +01:00
|
|
|
#ifdef VTYSH_DEBUG
|
2017-07-17 14:03:14 +02:00
|
|
|
printf("close vtysh\n");
|
2002-12-13 21:15:29 +01:00
|
|
|
#endif /* VTYSH_DEBUG */
|
2017-07-17 14:03:14 +02:00
|
|
|
return 0;
|
|
|
|
}
|
2002-12-13 21:15:29 +01:00
|
|
|
|
|
|
|
#ifdef VTYSH_DEBUG
|
2017-07-17 14:03:14 +02:00
|
|
|
printf("line: %.*s\n", nbytes, buf);
|
2002-12-13 21:15:29 +01:00
|
|
|
#endif /* VTYSH_DEBUG */
|
|
|
|
|
2017-07-17 14:03:14 +02:00
|
|
|
if (vty->length + nbytes >= VTY_BUFSIZ) {
|
|
|
|
/* Clear command line buffer. */
|
|
|
|
vty->cp = vty->length = 0;
|
|
|
|
vty_clear_buf(vty);
|
|
|
|
vty_out(vty, "%% Command is too long.\n");
|
|
|
|
} else {
|
|
|
|
for (p = buf; p < buf + nbytes; p++) {
|
|
|
|
vty->buf[vty->length++] = *p;
|
|
|
|
if (*p == '\0') {
|
|
|
|
/* Pass this line to parser. */
|
|
|
|
ret = vty_execute(vty);
|
|
|
|
/* Note that vty_execute clears the command buffer and resets
|
|
|
|
vty->length to 0. */
|
|
|
|
|
|
|
|
/* Return result. */
|
2002-12-13 21:15:29 +01:00
|
|
|
#ifdef VTYSH_DEBUG
|
2017-07-17 14:03:14 +02:00
|
|
|
printf("result: %d\n", ret);
|
|
|
|
printf("vtysh node: %d\n", vty->node);
|
2002-12-13 21:15:29 +01:00
|
|
|
#endif /* VTYSH_DEBUG */
|
|
|
|
|
2017-07-17 14:03:14 +02:00
|
|
|
/* hack for asynchronous "write integrated"
|
|
|
|
* - other commands in "buf" will be ditched
|
|
|
|
* - input during pending config-write is
|
|
|
|
* "unsupported" */
|
|
|
|
if (ret == CMD_SUSPEND)
|
|
|
|
break;
|
2016-11-09 14:15:34 +01:00
|
|
|
|
2017-07-17 14:03:14 +02:00
|
|
|
/* warning: watchfrr hardcodes this result write
|
|
|
|
*/
|
|
|
|
header[3] = ret;
|
|
|
|
buffer_put(vty->obuf, header, 4);
|
2005-02-23 Andrew J. Schorr <ajschorr@alumni.princeton.edu>
* buffer.h: Make the struct buffer and struct buffer_data structures
private by moving them inside buffer.c. Add comments for all
functions. Rename buffer_write as buffer_put (to be more consistent
with the buffer_putc and buffer_putstr functions). Declare a new
buffer_write function that is used to write data to a file descriptor
and/or add it to the buffer queue. Remove unused function
buffer_flush_vty_all. Create a new enum typedef buffer_status_t
to be used as the return code for all buffer_flush* functions
and buffer_write.
* buffer.c: The struct buffer and struct buffer_data declarations
are now private to this file. In conjunction with that, remove
some unnecessary fields: struct buffer (alloc, unused_head,
unused_tail, length), struct buffer_data (prev).
(buffer_data_new) Removed: functionality incorporated into buffer_add.
(buffer_data_free) Removed: use a macro BUFFER_DATA_FREE instead.
(buffer_new) Use calloc instead of malloc + memset(zero).
Supply an appropriate default size if the specified size is 0.
(buffer_free) Eliminate code duplication by calling buffer_reset to
free the contents of the buffer (and remove unused code related
to unused_head).
(buffer_empty,buffer_putc,buffer_putstr) Aesthetic change (make more
compact).
(buffer_reset) Use macro BUFFER_DATA_FREE. No need to set
alloc and length to 0 (these fields have been removed).
(buffer_add) Fix scope to be static. Call XMALLOC directly instead
of calling removed buffer_data_new function. Simplify the logic
(since it's now a singly-linked list instead of doubly-linked).
(buffer_write) Renamed to buffer_put. Change to void, since return
code of 1 was meaningless. No need to adjust length field, since
it has been removed.
(buffer_putw,buffer_flush,buffer_flush_vty_all,buffer_flush_vty)
Remove unused functions.
(buffer_flush_all) Rewrite using buffer_flush_available to eliminate
a possible failure mode if IOV_MAX is less than the number of buffers
on the queue.
(buffer_flush_window) Incorporate logic from buffer_flush_vty.
Log an error message if there is a writev error.
(buffer_flush_available) Be more paranoid: check for case where
buffer is already empty. Use new ERRNO_IO_RETRY macro, and use
new enum for return codes. Simplify deletion logic (since it's
now a singly-linked list).
(buffer_write) New function for use with non-blocking I/O.
* vty.h: Replace the struct vty sb_buffer field with a fixed-size
(5-character) sb_buf field and an sb_len field, since using
a struct buffer was inappropriate for this task. Add some useful
comments about telnet window size negotiation.
* vty.c: Include <arpa/telnet.h> (no longer included by zebra.h).
Remove VTY_OBUF_SIZE (instead use buffer_new default size).
Make telnet_backward_char and telnet_space_char static const.
(vty_out) Replace buffer_write with buffer_put.
(vty_log_out) Check for I/O errors. If fatal, close the vty session.
Consolidate 3 separate writes into a single write call.
(vty_will_echo,vty_command,vty_next_line,vty_previous_line,
vty_end_config,vty_describe_fold,vty_clear_buf,vty_serv_sock_addrinfo,
vty_serv_sock_family,vty_serv_un,vty_use_backup_config,exec_timeout,
vty_config_write,vty_save_cwd) Fix scope to static.
(vty_new) Let buffer_new use its default buffer size.
(vty_write) Fix signature: 2nd arg should be const char *.
Replaced buffer_write with buffer_put.
(vty_telnet_option) Fix minor bug (window height or width greater than
255 was broken). Use sb_buf and sb_len instead of removed sb_buffer
(which was being used improperly).
(vty_read) On error, use ERRNO_IO_RETRY to decide whether it's fatal.
If the error is fatal, call buffer_reset so vty_close does not attempt
to flush the data. Use new sb_buf and sb_len instead of sb_buffer
to store the SB negotiation string.
(vty_flush) When vty->lines is 0, call buffer_flush_available instead
of buffer_flush_window. Look at the return code from buffer_flush
to detect I/O errors (and in that case, log an error message and
close the vty).
(vty_create) Fix scope to static. Initialize sb_len to 0 instead
of creating sb_buffer.
(vty_accept) Set socket nonblocking.
(vtysh_accept) Use new set_nonblocking function instead of calling
fcntl directly.
(vtysh_flush) New function called from vtysh_read (after command
execution) and from vtysh_write. This flushes the buffer
and reacts appropriately to the return code (by closing the vty
or scheduling further flushes).
(vtysh_read) Check whether error is fatal using ERRNO_IO_RETRY.
If not, just try again later. Otherwise, call buffer_reset before
calling vty_close (to avoid trying to flush the buffer in vty_close).
Fix logic to allow case where a command does not arrive atomically
in a single read call by checking for the terminating NUL char.
(vtysh_write) Use new vtysh_flush helper function.
(vty_close) No need to call buffer_empty, just call buffer_flush_all
in any case (it will check whether the buffer is empty).
Do not free sb_buffer (since it has been removed).
(vty_log_fixed) Use writev instead of write.
* zebra.h: Do not include <arpa/telnet.h>, since this is used only
by lib/vty.c.
2005-02-23 16:12:34 +01:00
|
|
|
|
2017-07-17 14:03:14 +02:00
|
|
|
if (!vty->t_write && (vtysh_flush(vty) < 0))
|
|
|
|
/* Try to flush results; exit if a write
|
|
|
|
* error occurs. */
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2002-12-13 21:15:29 +01:00
|
|
|
|
2017-07-17 14:03:14 +02:00
|
|
|
if (vty->status == VTY_CLOSE)
|
|
|
|
vty_close(vty);
|
|
|
|
else
|
|
|
|
vty_event(VTYSH_READ, sock, vty);
|
2002-12-13 21:15:29 +01:00
|
|
|
|
2017-07-17 14:03:14 +02:00
|
|
|
return 0;
|
2002-12-13 21:15:29 +01:00
|
|
|
}
|
2004-11-04 20:26:16 +01:00
|
|
|
|
2017-07-17 14:03:14 +02:00
|
|
|
static int vtysh_write(struct thread *thread)
|
2004-11-04 20:26:16 +01:00
|
|
|
{
|
2017-07-17 14:03:14 +02:00
|
|
|
struct vty *vty = THREAD_ARG(thread);
|
2004-11-04 20:26:16 +01:00
|
|
|
|
2017-07-17 14:03:14 +02:00
|
|
|
vty->t_write = NULL;
|
|
|
|
vtysh_flush(vty);
|
|
|
|
return 0;
|
2004-11-04 20:26:16 +01:00
|
|
|
}
|
|
|
|
|
2002-12-13 21:15:29 +01:00
|
|
|
#endif /* VTYSH */
|
|
|
|
|
|
|
|
/* Determine address family to bind. */
|
2017-07-17 14:03:14 +02:00
|
|
|
void vty_serv_sock(const char *addr, unsigned short port, const char *path)
|
2002-12-13 21:15:29 +01:00
|
|
|
{
|
2017-07-17 14:03:14 +02:00
|
|
|
/* If port is set to 0, do not listen on TCP/IP at all! */
|
|
|
|
if (port)
|
|
|
|
vty_serv_sock_addrinfo(addr, port);
|
2002-12-13 21:15:29 +01:00
|
|
|
|
|
|
|
#ifdef VTYSH
|
2017-07-17 14:03:14 +02:00
|
|
|
vty_serv_un(path);
|
2002-12-13 21:15:29 +01:00
|
|
|
#endif /* VTYSH */
|
|
|
|
}
|
|
|
|
|
2006-07-11 02:06:49 +02:00
|
|
|
/* Close vty interface. Warning: call this only from functions that
|
|
|
|
will be careful not to access the vty afterwards (since it has
|
|
|
|
now been freed). This is safest from top-level functions (called
|
|
|
|
directly by the thread dispatcher). */
|
2017-07-17 14:03:14 +02:00
|
|
|
void vty_close(struct vty *vty)
|
2002-12-13 21:15:29 +01:00
|
|
|
{
|
2017-07-17 14:03:14 +02:00
|
|
|
int i;
|
|
|
|
bool was_stdio = false;
|
2002-12-13 21:15:29 +01:00
|
|
|
|
2017-07-17 14:03:14 +02:00
|
|
|
/* Cancel threads.*/
|
|
|
|
if (vty->t_read)
|
|
|
|
thread_cancel(vty->t_read);
|
|
|
|
if (vty->t_write)
|
|
|
|
thread_cancel(vty->t_write);
|
|
|
|
if (vty->t_timeout)
|
|
|
|
thread_cancel(vty->t_timeout);
|
2002-12-13 21:15:29 +01:00
|
|
|
|
2017-07-17 14:03:14 +02:00
|
|
|
/* Flush buffer. */
|
|
|
|
buffer_flush_all(vty->obuf, vty->wfd);
|
2002-12-13 21:15:29 +01:00
|
|
|
|
2017-07-17 14:03:14 +02:00
|
|
|
/* Free input buffer. */
|
|
|
|
buffer_free(vty->obuf);
|
2018-06-06 21:09:44 +02:00
|
|
|
buffer_free(vty->lbuf);
|
2002-12-13 21:15:29 +01:00
|
|
|
|
2017-07-17 14:03:14 +02:00
|
|
|
/* Free command history. */
|
|
|
|
for (i = 0; i < VTY_MAXHIST; i++)
|
|
|
|
if (vty->hist[i])
|
|
|
|
XFREE(MTYPE_VTY_HIST, vty->hist[i]);
|
2002-12-13 21:15:29 +01:00
|
|
|
|
2017-07-17 14:03:14 +02:00
|
|
|
/* Unset vector. */
|
2017-08-04 12:05:38 +02:00
|
|
|
if (vty->fd != -1)
|
|
|
|
vector_unset(vtyvec, vty->fd);
|
2002-12-13 21:15:29 +01:00
|
|
|
|
2017-07-17 14:03:14 +02:00
|
|
|
if (vty->wfd > 0 && vty->type == VTY_FILE)
|
|
|
|
fsync(vty->wfd);
|
2017-02-08 16:14:10 +01:00
|
|
|
|
2017-08-04 12:05:38 +02:00
|
|
|
/* Close socket.
|
|
|
|
* note check is for fd > STDERR_FILENO, not fd != -1.
|
|
|
|
* We never close stdin/stdout/stderr here, because we may be
|
|
|
|
* running in foreground mode with logging to stdout. Also,
|
|
|
|
* additionally, we'd need to replace these fds with /dev/null. */
|
|
|
|
if (vty->wfd > STDERR_FILENO && vty->wfd != vty->fd)
|
|
|
|
close(vty->wfd);
|
2018-08-08 20:52:15 +02:00
|
|
|
if (vty->fd > STDERR_FILENO)
|
2017-07-17 14:03:14 +02:00
|
|
|
close(vty->fd);
|
2018-08-08 20:52:15 +02:00
|
|
|
if (vty->fd == STDIN_FILENO)
|
2017-07-17 14:03:14 +02:00
|
|
|
was_stdio = true;
|
2002-12-13 21:15:29 +01:00
|
|
|
|
2017-07-17 14:03:14 +02:00
|
|
|
if (vty->buf)
|
|
|
|
XFREE(MTYPE_VTY, vty->buf);
|
2002-12-13 21:15:29 +01:00
|
|
|
|
2017-07-17 14:03:14 +02:00
|
|
|
if (vty->error_buf)
|
|
|
|
XFREE(MTYPE_VTY, vty->error_buf);
|
2015-07-28 05:30:22 +02:00
|
|
|
|
2017-07-17 14:03:14 +02:00
|
|
|
/* Check configure. */
|
|
|
|
vty_config_unlock(vty);
|
2002-12-13 21:15:29 +01:00
|
|
|
|
2017-07-17 14:03:14 +02:00
|
|
|
/* OK free vty. */
|
|
|
|
XFREE(MTYPE_VTY, vty);
|
2017-01-26 22:09:37 +01:00
|
|
|
|
2017-07-17 14:03:14 +02:00
|
|
|
if (was_stdio)
|
2017-06-01 16:02:23 +02:00
|
|
|
vty_stdio_reset(0);
|
2002-12-13 21:15:29 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/* When time out occur output message then close connection. */
|
2017-07-17 14:03:14 +02:00
|
|
|
static int vty_timeout(struct thread *thread)
|
2002-12-13 21:15:29 +01:00
|
|
|
{
|
2017-07-17 14:03:14 +02:00
|
|
|
struct vty *vty;
|
2002-12-13 21:15:29 +01:00
|
|
|
|
2017-07-17 14:03:14 +02:00
|
|
|
vty = THREAD_ARG(thread);
|
|
|
|
vty->t_timeout = NULL;
|
|
|
|
vty->v_timeout = 0;
|
2002-12-13 21:15:29 +01:00
|
|
|
|
2017-07-17 14:03:14 +02:00
|
|
|
/* Clear buffer*/
|
2018-06-06 21:09:44 +02:00
|
|
|
buffer_reset(vty->lbuf);
|
2017-07-17 14:03:14 +02:00
|
|
|
buffer_reset(vty->obuf);
|
|
|
|
vty_out(vty, "\nVty connection is timed out.\n");
|
2002-12-13 21:15:29 +01:00
|
|
|
|
2017-07-17 14:03:14 +02:00
|
|
|
/* Close connection. */
|
|
|
|
vty->status = VTY_CLOSE;
|
|
|
|
vty_close(vty);
|
2002-12-13 21:15:29 +01:00
|
|
|
|
2017-07-17 14:03:14 +02:00
|
|
|
return 0;
|
2002-12-13 21:15:29 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Read up configuration file from file_name. */
|
2017-07-17 14:03:14 +02:00
|
|
|
static void vty_read_file(FILE *confp)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
struct vty *vty;
|
|
|
|
unsigned int line_num = 0;
|
|
|
|
|
|
|
|
vty = vty_new();
|
2017-08-04 12:05:38 +02:00
|
|
|
/* vty_close won't close stderr; if some config command prints
|
|
|
|
* something it'll end up there. (not ideal; it'd be beter if output
|
|
|
|
* from a file-load went to logging instead. Also note that if this
|
|
|
|
* function is called after daemonizing, stderr will be /dev/null.)
|
|
|
|
*
|
|
|
|
* vty->fd will be -1 from vty_new()
|
|
|
|
*/
|
|
|
|
vty->wfd = STDERR_FILENO;
|
2017-07-17 14:03:14 +02:00
|
|
|
vty->type = VTY_FILE;
|
|
|
|
vty->node = CONFIG_NODE;
|
|
|
|
|
|
|
|
/* Execute configuration file */
|
|
|
|
ret = config_from_file(vty, confp, &line_num);
|
|
|
|
|
|
|
|
/* Flush any previous errors before printing messages below */
|
2017-08-04 12:05:38 +02:00
|
|
|
buffer_flush_all(vty->obuf, vty->wfd);
|
2017-07-17 14:03:14 +02:00
|
|
|
|
|
|
|
if (!((ret == CMD_SUCCESS) || (ret == CMD_ERR_NOTHING_TODO))) {
|
|
|
|
const char *message = NULL;
|
2017-08-01 00:05:04 +02:00
|
|
|
char *nl;
|
|
|
|
|
2017-07-17 14:03:14 +02:00
|
|
|
switch (ret) {
|
|
|
|
case CMD_ERR_AMBIGUOUS:
|
2017-08-01 00:05:04 +02:00
|
|
|
message = "Ambiguous command";
|
2017-07-17 14:03:14 +02:00
|
|
|
break;
|
|
|
|
case CMD_ERR_NO_MATCH:
|
2017-08-01 00:05:04 +02:00
|
|
|
message = "No such command";
|
2017-07-17 14:03:14 +02:00
|
|
|
break;
|
2017-10-26 04:35:35 +02:00
|
|
|
case CMD_WARNING:
|
|
|
|
message = "Command returned Warning";
|
|
|
|
break;
|
|
|
|
case CMD_WARNING_CONFIG_FAILED:
|
|
|
|
message = "Command returned Warning Config Failed";
|
|
|
|
break;
|
|
|
|
case CMD_ERR_INCOMPLETE:
|
|
|
|
message = "Command returned Incomplete";
|
|
|
|
break;
|
|
|
|
case CMD_ERR_EXEED_ARGC_MAX:
|
2018-03-06 20:02:52 +01:00
|
|
|
message =
|
|
|
|
"Command exceeded maximum number of Arguments";
|
2017-10-26 04:35:35 +02:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
message = "Command returned unhandled error message";
|
|
|
|
break;
|
2017-07-17 14:03:14 +02:00
|
|
|
}
|
2017-08-01 00:05:04 +02:00
|
|
|
|
|
|
|
nl = strchr(vty->error_buf, '\n');
|
|
|
|
if (nl)
|
|
|
|
*nl = '\0';
|
2018-06-14 15:21:11 +02:00
|
|
|
zlog_ferr(LIB_ERR_VTY,
|
|
|
|
"ERROR: %s on config line %u: %s", message, line_num,
|
|
|
|
vty->error_buf);
|
2017-07-17 14:03:14 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
vty_close(vty);
|
|
|
|
}
|
|
|
|
|
|
|
|
static FILE *vty_use_backup_config(const char *fullpath)
|
|
|
|
{
|
|
|
|
char *fullpath_sav, *fullpath_tmp;
|
|
|
|
FILE *ret = NULL;
|
|
|
|
int tmp, sav;
|
|
|
|
int c;
|
|
|
|
char buffer[512];
|
|
|
|
|
|
|
|
fullpath_sav = malloc(strlen(fullpath) + strlen(CONF_BACKUP_EXT) + 1);
|
|
|
|
strcpy(fullpath_sav, fullpath);
|
|
|
|
strcat(fullpath_sav, CONF_BACKUP_EXT);
|
|
|
|
|
|
|
|
sav = open(fullpath_sav, O_RDONLY);
|
|
|
|
if (sav < 0) {
|
|
|
|
free(fullpath_sav);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
fullpath_tmp = malloc(strlen(fullpath) + 8);
|
|
|
|
sprintf(fullpath_tmp, "%s.XXXXXX", fullpath);
|
|
|
|
|
|
|
|
/* Open file to configuration write. */
|
|
|
|
tmp = mkstemp(fullpath_tmp);
|
|
|
|
if (tmp < 0)
|
|
|
|
goto out_close_sav;
|
|
|
|
|
|
|
|
if (fchmod(tmp, CONFIGFILE_MASK) != 0)
|
|
|
|
goto out_close;
|
|
|
|
|
|
|
|
while ((c = read(sav, buffer, 512)) > 0) {
|
|
|
|
if (write(tmp, buffer, c) <= 0)
|
|
|
|
goto out_close;
|
|
|
|
}
|
|
|
|
close(sav);
|
|
|
|
close(tmp);
|
|
|
|
|
|
|
|
if (rename(fullpath_tmp, fullpath) == 0)
|
|
|
|
ret = fopen(fullpath, "r");
|
|
|
|
else
|
|
|
|
unlink(fullpath_tmp);
|
|
|
|
|
|
|
|
if (0) {
|
|
|
|
out_close:
|
|
|
|
close(tmp);
|
|
|
|
unlink(fullpath_tmp);
|
|
|
|
out_close_sav:
|
|
|
|
close(sav);
|
|
|
|
}
|
|
|
|
|
|
|
|
free(fullpath_sav);
|
|
|
|
free(fullpath_tmp);
|
|
|
|
return ret;
|
2002-12-13 21:15:29 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Read up configuration file from file_name. */
|
2018-05-08 02:02:39 +02:00
|
|
|
bool vty_read_config(const char *config_file, char *config_default_dir)
|
2017-07-17 14:03:14 +02:00
|
|
|
{
|
|
|
|
char cwd[MAXPATHLEN];
|
|
|
|
FILE *confp = NULL;
|
|
|
|
const char *fullpath;
|
|
|
|
char *tmp = NULL;
|
2018-05-08 02:02:39 +02:00
|
|
|
bool read_success = false;
|
2017-07-17 14:03:14 +02:00
|
|
|
|
|
|
|
/* If -f flag specified. */
|
|
|
|
if (config_file != NULL) {
|
|
|
|
if (!IS_DIRECTORY_SEP(config_file[0])) {
|
|
|
|
if (getcwd(cwd, MAXPATHLEN) == NULL) {
|
2018-06-14 15:13:18 +02:00
|
|
|
zlog_ferr(LIB_ERR_SYSTEM_CALL,
|
|
|
|
"Failure to determine Current Working Directory %d!",
|
|
|
|
errno);
|
2017-07-17 14:03:14 +02:00
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
tmp = XMALLOC(MTYPE_TMP,
|
|
|
|
strlen(cwd) + strlen(config_file) + 2);
|
|
|
|
sprintf(tmp, "%s/%s", cwd, config_file);
|
|
|
|
fullpath = tmp;
|
|
|
|
} else
|
|
|
|
fullpath = config_file;
|
|
|
|
|
|
|
|
confp = fopen(fullpath, "r");
|
|
|
|
|
|
|
|
if (confp == NULL) {
|
2018-06-14 15:13:18 +02:00
|
|
|
zlog_warn("%s: failed to open configuration file %s: %s, checking backup",
|
2018-03-06 20:02:52 +01:00
|
|
|
__func__, fullpath, safe_strerror(errno));
|
2017-07-17 14:03:14 +02:00
|
|
|
|
|
|
|
confp = vty_use_backup_config(fullpath);
|
|
|
|
if (confp)
|
2018-03-06 20:02:52 +01:00
|
|
|
zlog_warn(
|
|
|
|
"WARNING: using backup configuration file!");
|
2017-07-17 14:03:14 +02:00
|
|
|
else {
|
2018-06-14 15:21:11 +02:00
|
|
|
zlog_ferr(LIB_ERR_VTY,
|
|
|
|
"can't open configuration file [%s]",
|
|
|
|
config_file);
|
2017-07-17 14:03:14 +02:00
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
|
|
|
|
host_config_set(config_default_dir);
|
2015-05-20 03:29:18 +02:00
|
|
|
|
2002-12-13 21:15:29 +01:00
|
|
|
#ifdef VTYSH
|
2017-07-17 14:03:14 +02:00
|
|
|
int ret;
|
|
|
|
struct stat conf_stat;
|
|
|
|
|
|
|
|
/* !!!!PLEASE LEAVE!!!!
|
|
|
|
* This is NEEDED for use with vtysh -b, or else you can get
|
|
|
|
* a real configuration food fight with a lot garbage in the
|
|
|
|
* merged configuration file it creates coming from the per
|
|
|
|
* daemon configuration files. This also allows the daemons
|
|
|
|
* to start if there default configuration file is not
|
|
|
|
* present or ignore them, as needed when using vtysh -b to
|
|
|
|
* configure the daemons at boot - MAG
|
|
|
|
*/
|
|
|
|
|
|
|
|
/* Stat for vtysh Zebra.conf, if found startup and wait for
|
|
|
|
* boot configuration
|
|
|
|
*/
|
|
|
|
|
|
|
|
if (strstr(config_default_dir, "vtysh") == NULL) {
|
|
|
|
ret = stat(integrate_default, &conf_stat);
|
2018-05-08 02:02:39 +02:00
|
|
|
if (ret >= 0) {
|
|
|
|
read_success = true;
|
2017-07-17 14:03:14 +02:00
|
|
|
goto tmp_free_and_out;
|
2018-05-08 02:02:39 +02:00
|
|
|
}
|
2017-07-17 14:03:14 +02:00
|
|
|
}
|
2015-05-20 03:29:18 +02:00
|
|
|
#endif /* VTYSH */
|
2017-07-17 14:03:14 +02:00
|
|
|
confp = fopen(config_default_dir, "r");
|
|
|
|
if (confp == NULL) {
|
2018-06-14 15:13:18 +02:00
|
|
|
zlog_warn("%s: failed to open configuration file %s: %s, checking backup",
|
|
|
|
__func__, config_default_dir,
|
|
|
|
safe_strerror(errno));
|
2017-07-17 14:03:14 +02:00
|
|
|
|
|
|
|
confp = vty_use_backup_config(config_default_dir);
|
|
|
|
if (confp) {
|
2018-03-06 20:02:52 +01:00
|
|
|
zlog_warn(
|
|
|
|
"WARNING: using backup configuration file!");
|
2017-07-17 14:03:14 +02:00
|
|
|
fullpath = config_default_dir;
|
|
|
|
} else {
|
2018-06-14 15:21:11 +02:00
|
|
|
zlog_ferr(LIB_ERR_VTY,
|
|
|
|
"can't open configuration file [%s]",
|
|
|
|
config_default_dir);
|
2017-07-17 14:03:14 +02:00
|
|
|
goto tmp_free_and_out;
|
|
|
|
}
|
|
|
|
} else
|
|
|
|
fullpath = config_default_dir;
|
|
|
|
}
|
|
|
|
|
|
|
|
vty_read_file(confp);
|
2018-05-08 02:02:39 +02:00
|
|
|
read_success = true;
|
2017-07-17 14:03:14 +02:00
|
|
|
|
|
|
|
fclose(confp);
|
|
|
|
|
|
|
|
host_config_set(fullpath);
|
2016-03-07 20:57:16 +01:00
|
|
|
|
|
|
|
tmp_free_and_out:
|
2017-07-17 14:03:14 +02:00
|
|
|
if (tmp)
|
|
|
|
XFREE(MTYPE_TMP, tmp);
|
2018-05-08 02:02:39 +02:00
|
|
|
|
|
|
|
return read_success;
|
2002-12-13 21:15:29 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Small utility function which output log to the VTY. */
|
2017-07-17 14:03:14 +02:00
|
|
|
void vty_log(const char *level, const char *proto_str, const char *format,
|
|
|
|
struct timestamp_control *ctl, va_list va)
|
2002-12-13 21:15:29 +01:00
|
|
|
{
|
2017-07-17 14:03:14 +02:00
|
|
|
unsigned int i;
|
|
|
|
struct vty *vty;
|
2016-09-20 01:46:51 +02:00
|
|
|
|
2017-07-17 14:03:14 +02:00
|
|
|
if (!vtyvec)
|
|
|
|
return;
|
2002-12-13 21:15:29 +01:00
|
|
|
|
2017-07-17 14:03:14 +02:00
|
|
|
for (i = 0; i < vector_active(vtyvec); i++)
|
|
|
|
if ((vty = vector_slot(vtyvec, i)) != NULL)
|
|
|
|
if (vty->monitor) {
|
|
|
|
va_list ac;
|
|
|
|
va_copy(ac, va);
|
|
|
|
vty_log_out(vty, level, proto_str, format, ctl,
|
|
|
|
ac);
|
|
|
|
va_end(ac);
|
|
|
|
}
|
2002-12-13 21:15:29 +01:00
|
|
|
}
|
|
|
|
|
2004-12-07 Andrew J. Schorr <ajschorr@alumni.princeton.edu>
* bgp_main.c: (main) The 2nd argument to openzlog has been removed.
* isis_main.c: (main) The 2nd argument to openzlog has been removed.
* ospf6_main.c: (main) The 2nd argument to openzlog has been removed.
Note that stdout logging will no longer be enabled by default when
not running as a daemon.
* ospf_main.c: (main) The 2nd argument to openzlog has been removed.
* rip_main.c: (main) The 2nd argument to openzlog has been removed.
* ripng_main.c: (main) The 2nd argument to openzlog has been removed.
* main.c: (main) The 2nd argument to openzlog has been removed.
So stdout logging will no longer be enabled by default.
* irdp_main.c: (irdp_finish) Reduce severity of shutdown message
from LOG_WARNING to LOG_INFO.
* vtysh.c: Make several functions static instead of global.
Added several commands to support destination-specific logging levels.
(vtysh_completion) This function is unused, so comment it out.
* basic.texi: Document new logging features. Separate basic config
commands from basic VTY commands.
* log.h: Replace struct zlog flags and maskpri fields with maxlvl
array to support individual logging levels for each destination.
Remove the 2nd argument to openzlog since the default logging config
should be standardized inside the library. Replaced the
zlog_set_flag and zlog_reset_flag functions with zlog_set_level.
And zlog_set_file now requires an additional log_level argument.
Declare zlog_proto_names for use inside command.c in the
"show logging" command. Added defines useful for command
construction.
* log.c: (vzlog) Decide where to send the message based on the
individual logging levels configured for each destination.
Remove support for ZLOG_STDERR since it was never actually used.
Support record-priority for terminal monitors.
(zlog_signal,zlog_backtrace_sigsafe) Support destination-specific
logging levels. Remove stderr support (was never used). Added
support for terminal monitor logging.
(_zlog_assert_failed) Increase message severity to LOG_EMERG.
(openzlog) Remove 2nd argument since default config should be
standardized in library. By default, terminal monitoring
is set to debug, and all other logging is disabled.
(zlog_set_flag,zlog_reset_flag) Removed.
(zlog_set_level) New function to replace zlog_set_flag and
zlog_reset_flag. Supports destination-specific logging levels.
(zlog_set_file,zlog_reset_file) Support file-specific logging level.
(zlog_rotate) Log an error message if fopen fails, and support
new file-specific logging level.
* command.h: Change DEFUN_CMD_FUNC_DECL and DEFUN_CMD_FUNC_TEXT so that
command functions will be static instead of global. Remove
declarations for config_exit and config_help. Define new macros
DEFUNSH_ATTR, DEFUNSH_HIDDEN, and DEFUNSH_DEPRECATED so we can
have deprecated commands in vtysh. Similarly, for completeness,
define macros ALIAS_SH, ALIAS_SH_HIDDEN, and ALIAS_SH_DEPRECATED.
Also, fix bug in ALIAS_ATTR macro (didn't matter because it
was never used).
* command.c: Make many functions static instead of global.
(facility_name,facility_match,level_match) New functions
to support enhanced destination-specific logging levels.
(config_write_host) Support new destination-specific logging levels.
(config_logmsg) Added new "logmsg" command to help test logging
system.
(show_logging) Added "show logging" command to show the current
configuration of the logging system.
(config_log_stdout_level) Support explicit stdout logging level.
(no_config_log_stdout) Now takes optional LEVEL arg.
(config_log_monitor,config_log_monitor_level,no_config_log_monitor)
New commands creating new "log monitor" commands to set terminal
monitoring log level.
(config_log_file_level) Support explicit file logging level.
(config_log_syslog_level) Support explicit syslog logging level.
(config_log_facility,no_config_log_facility) Implement new
"log facility" command.
(cmd_init) Add hooks for new commands: "show logging", "logmsg",
"log stdout <level>", "log monitor", "log monitor <level>",
"no log monitor", "log file <filename> <level>",
"no log file <filename> <level>", "log syslog <level>",
"log facility", and "no log facility".
* vty.h: Added a "level" argument to vty_log so it can support
"log record-priority". Declare new function vty_log_fixed for
use in signal handlers.
* vty.c: (vty_log,vty_log_out) Added a "level" argument to support
"log record-priority" for vty terminal monitors.
(vty_down_level) Use config_exit_cmd.func instead of calling
config_exit directly (since command functions will now be static
instead of global).
(vty_log_fixed) New function to send terminal monitor messages
from inside a signal handler.
2004-12-07 16:39:31 +01:00
|
|
|
/* Async-signal-safe version of vty_log for fixed strings. */
|
2017-07-17 14:03:14 +02:00
|
|
|
void vty_log_fixed(char *buf, size_t len)
|
2004-12-07 Andrew J. Schorr <ajschorr@alumni.princeton.edu>
* bgp_main.c: (main) The 2nd argument to openzlog has been removed.
* isis_main.c: (main) The 2nd argument to openzlog has been removed.
* ospf6_main.c: (main) The 2nd argument to openzlog has been removed.
Note that stdout logging will no longer be enabled by default when
not running as a daemon.
* ospf_main.c: (main) The 2nd argument to openzlog has been removed.
* rip_main.c: (main) The 2nd argument to openzlog has been removed.
* ripng_main.c: (main) The 2nd argument to openzlog has been removed.
* main.c: (main) The 2nd argument to openzlog has been removed.
So stdout logging will no longer be enabled by default.
* irdp_main.c: (irdp_finish) Reduce severity of shutdown message
from LOG_WARNING to LOG_INFO.
* vtysh.c: Make several functions static instead of global.
Added several commands to support destination-specific logging levels.
(vtysh_completion) This function is unused, so comment it out.
* basic.texi: Document new logging features. Separate basic config
commands from basic VTY commands.
* log.h: Replace struct zlog flags and maskpri fields with maxlvl
array to support individual logging levels for each destination.
Remove the 2nd argument to openzlog since the default logging config
should be standardized inside the library. Replaced the
zlog_set_flag and zlog_reset_flag functions with zlog_set_level.
And zlog_set_file now requires an additional log_level argument.
Declare zlog_proto_names for use inside command.c in the
"show logging" command. Added defines useful for command
construction.
* log.c: (vzlog) Decide where to send the message based on the
individual logging levels configured for each destination.
Remove support for ZLOG_STDERR since it was never actually used.
Support record-priority for terminal monitors.
(zlog_signal,zlog_backtrace_sigsafe) Support destination-specific
logging levels. Remove stderr support (was never used). Added
support for terminal monitor logging.
(_zlog_assert_failed) Increase message severity to LOG_EMERG.
(openzlog) Remove 2nd argument since default config should be
standardized in library. By default, terminal monitoring
is set to debug, and all other logging is disabled.
(zlog_set_flag,zlog_reset_flag) Removed.
(zlog_set_level) New function to replace zlog_set_flag and
zlog_reset_flag. Supports destination-specific logging levels.
(zlog_set_file,zlog_reset_file) Support file-specific logging level.
(zlog_rotate) Log an error message if fopen fails, and support
new file-specific logging level.
* command.h: Change DEFUN_CMD_FUNC_DECL and DEFUN_CMD_FUNC_TEXT so that
command functions will be static instead of global. Remove
declarations for config_exit and config_help. Define new macros
DEFUNSH_ATTR, DEFUNSH_HIDDEN, and DEFUNSH_DEPRECATED so we can
have deprecated commands in vtysh. Similarly, for completeness,
define macros ALIAS_SH, ALIAS_SH_HIDDEN, and ALIAS_SH_DEPRECATED.
Also, fix bug in ALIAS_ATTR macro (didn't matter because it
was never used).
* command.c: Make many functions static instead of global.
(facility_name,facility_match,level_match) New functions
to support enhanced destination-specific logging levels.
(config_write_host) Support new destination-specific logging levels.
(config_logmsg) Added new "logmsg" command to help test logging
system.
(show_logging) Added "show logging" command to show the current
configuration of the logging system.
(config_log_stdout_level) Support explicit stdout logging level.
(no_config_log_stdout) Now takes optional LEVEL arg.
(config_log_monitor,config_log_monitor_level,no_config_log_monitor)
New commands creating new "log monitor" commands to set terminal
monitoring log level.
(config_log_file_level) Support explicit file logging level.
(config_log_syslog_level) Support explicit syslog logging level.
(config_log_facility,no_config_log_facility) Implement new
"log facility" command.
(cmd_init) Add hooks for new commands: "show logging", "logmsg",
"log stdout <level>", "log monitor", "log monitor <level>",
"no log monitor", "log file <filename> <level>",
"no log file <filename> <level>", "log syslog <level>",
"log facility", and "no log facility".
* vty.h: Added a "level" argument to vty_log so it can support
"log record-priority". Declare new function vty_log_fixed for
use in signal handlers.
* vty.c: (vty_log,vty_log_out) Added a "level" argument to support
"log record-priority" for vty terminal monitors.
(vty_down_level) Use config_exit_cmd.func instead of calling
config_exit directly (since command functions will now be static
instead of global).
(vty_log_fixed) New function to send terminal monitor messages
from inside a signal handler.
2004-12-07 16:39:31 +01:00
|
|
|
{
|
2017-07-17 14:03:14 +02:00
|
|
|
unsigned int i;
|
|
|
|
struct iovec iov[2];
|
|
|
|
char crlf[4] = "\r\n";
|
2005-02-23 Andrew J. Schorr <ajschorr@alumni.princeton.edu>
* buffer.h: Make the struct buffer and struct buffer_data structures
private by moving them inside buffer.c. Add comments for all
functions. Rename buffer_write as buffer_put (to be more consistent
with the buffer_putc and buffer_putstr functions). Declare a new
buffer_write function that is used to write data to a file descriptor
and/or add it to the buffer queue. Remove unused function
buffer_flush_vty_all. Create a new enum typedef buffer_status_t
to be used as the return code for all buffer_flush* functions
and buffer_write.
* buffer.c: The struct buffer and struct buffer_data declarations
are now private to this file. In conjunction with that, remove
some unnecessary fields: struct buffer (alloc, unused_head,
unused_tail, length), struct buffer_data (prev).
(buffer_data_new) Removed: functionality incorporated into buffer_add.
(buffer_data_free) Removed: use a macro BUFFER_DATA_FREE instead.
(buffer_new) Use calloc instead of malloc + memset(zero).
Supply an appropriate default size if the specified size is 0.
(buffer_free) Eliminate code duplication by calling buffer_reset to
free the contents of the buffer (and remove unused code related
to unused_head).
(buffer_empty,buffer_putc,buffer_putstr) Aesthetic change (make more
compact).
(buffer_reset) Use macro BUFFER_DATA_FREE. No need to set
alloc and length to 0 (these fields have been removed).
(buffer_add) Fix scope to be static. Call XMALLOC directly instead
of calling removed buffer_data_new function. Simplify the logic
(since it's now a singly-linked list instead of doubly-linked).
(buffer_write) Renamed to buffer_put. Change to void, since return
code of 1 was meaningless. No need to adjust length field, since
it has been removed.
(buffer_putw,buffer_flush,buffer_flush_vty_all,buffer_flush_vty)
Remove unused functions.
(buffer_flush_all) Rewrite using buffer_flush_available to eliminate
a possible failure mode if IOV_MAX is less than the number of buffers
on the queue.
(buffer_flush_window) Incorporate logic from buffer_flush_vty.
Log an error message if there is a writev error.
(buffer_flush_available) Be more paranoid: check for case where
buffer is already empty. Use new ERRNO_IO_RETRY macro, and use
new enum for return codes. Simplify deletion logic (since it's
now a singly-linked list).
(buffer_write) New function for use with non-blocking I/O.
* vty.h: Replace the struct vty sb_buffer field with a fixed-size
(5-character) sb_buf field and an sb_len field, since using
a struct buffer was inappropriate for this task. Add some useful
comments about telnet window size negotiation.
* vty.c: Include <arpa/telnet.h> (no longer included by zebra.h).
Remove VTY_OBUF_SIZE (instead use buffer_new default size).
Make telnet_backward_char and telnet_space_char static const.
(vty_out) Replace buffer_write with buffer_put.
(vty_log_out) Check for I/O errors. If fatal, close the vty session.
Consolidate 3 separate writes into a single write call.
(vty_will_echo,vty_command,vty_next_line,vty_previous_line,
vty_end_config,vty_describe_fold,vty_clear_buf,vty_serv_sock_addrinfo,
vty_serv_sock_family,vty_serv_un,vty_use_backup_config,exec_timeout,
vty_config_write,vty_save_cwd) Fix scope to static.
(vty_new) Let buffer_new use its default buffer size.
(vty_write) Fix signature: 2nd arg should be const char *.
Replaced buffer_write with buffer_put.
(vty_telnet_option) Fix minor bug (window height or width greater than
255 was broken). Use sb_buf and sb_len instead of removed sb_buffer
(which was being used improperly).
(vty_read) On error, use ERRNO_IO_RETRY to decide whether it's fatal.
If the error is fatal, call buffer_reset so vty_close does not attempt
to flush the data. Use new sb_buf and sb_len instead of sb_buffer
to store the SB negotiation string.
(vty_flush) When vty->lines is 0, call buffer_flush_available instead
of buffer_flush_window. Look at the return code from buffer_flush
to detect I/O errors (and in that case, log an error message and
close the vty).
(vty_create) Fix scope to static. Initialize sb_len to 0 instead
of creating sb_buffer.
(vty_accept) Set socket nonblocking.
(vtysh_accept) Use new set_nonblocking function instead of calling
fcntl directly.
(vtysh_flush) New function called from vtysh_read (after command
execution) and from vtysh_write. This flushes the buffer
and reacts appropriately to the return code (by closing the vty
or scheduling further flushes).
(vtysh_read) Check whether error is fatal using ERRNO_IO_RETRY.
If not, just try again later. Otherwise, call buffer_reset before
calling vty_close (to avoid trying to flush the buffer in vty_close).
Fix logic to allow case where a command does not arrive atomically
in a single read call by checking for the terminating NUL char.
(vtysh_write) Use new vtysh_flush helper function.
(vty_close) No need to call buffer_empty, just call buffer_flush_all
in any case (it will check whether the buffer is empty).
Do not free sb_buffer (since it has been removed).
(vty_log_fixed) Use writev instead of write.
* zebra.h: Do not include <arpa/telnet.h>, since this is used only
by lib/vty.c.
2005-02-23 16:12:34 +01:00
|
|
|
|
2017-07-17 14:03:14 +02:00
|
|
|
/* vty may not have been initialised */
|
|
|
|
if (!vtyvec)
|
|
|
|
return;
|
2016-09-20 01:46:51 +02:00
|
|
|
|
2017-07-17 14:03:14 +02:00
|
|
|
iov[0].iov_base = buf;
|
|
|
|
iov[0].iov_len = len;
|
|
|
|
iov[1].iov_base = crlf;
|
|
|
|
iov[1].iov_len = 2;
|
2004-12-07 Andrew J. Schorr <ajschorr@alumni.princeton.edu>
* bgp_main.c: (main) The 2nd argument to openzlog has been removed.
* isis_main.c: (main) The 2nd argument to openzlog has been removed.
* ospf6_main.c: (main) The 2nd argument to openzlog has been removed.
Note that stdout logging will no longer be enabled by default when
not running as a daemon.
* ospf_main.c: (main) The 2nd argument to openzlog has been removed.
* rip_main.c: (main) The 2nd argument to openzlog has been removed.
* ripng_main.c: (main) The 2nd argument to openzlog has been removed.
* main.c: (main) The 2nd argument to openzlog has been removed.
So stdout logging will no longer be enabled by default.
* irdp_main.c: (irdp_finish) Reduce severity of shutdown message
from LOG_WARNING to LOG_INFO.
* vtysh.c: Make several functions static instead of global.
Added several commands to support destination-specific logging levels.
(vtysh_completion) This function is unused, so comment it out.
* basic.texi: Document new logging features. Separate basic config
commands from basic VTY commands.
* log.h: Replace struct zlog flags and maskpri fields with maxlvl
array to support individual logging levels for each destination.
Remove the 2nd argument to openzlog since the default logging config
should be standardized inside the library. Replaced the
zlog_set_flag and zlog_reset_flag functions with zlog_set_level.
And zlog_set_file now requires an additional log_level argument.
Declare zlog_proto_names for use inside command.c in the
"show logging" command. Added defines useful for command
construction.
* log.c: (vzlog) Decide where to send the message based on the
individual logging levels configured for each destination.
Remove support for ZLOG_STDERR since it was never actually used.
Support record-priority for terminal monitors.
(zlog_signal,zlog_backtrace_sigsafe) Support destination-specific
logging levels. Remove stderr support (was never used). Added
support for terminal monitor logging.
(_zlog_assert_failed) Increase message severity to LOG_EMERG.
(openzlog) Remove 2nd argument since default config should be
standardized in library. By default, terminal monitoring
is set to debug, and all other logging is disabled.
(zlog_set_flag,zlog_reset_flag) Removed.
(zlog_set_level) New function to replace zlog_set_flag and
zlog_reset_flag. Supports destination-specific logging levels.
(zlog_set_file,zlog_reset_file) Support file-specific logging level.
(zlog_rotate) Log an error message if fopen fails, and support
new file-specific logging level.
* command.h: Change DEFUN_CMD_FUNC_DECL and DEFUN_CMD_FUNC_TEXT so that
command functions will be static instead of global. Remove
declarations for config_exit and config_help. Define new macros
DEFUNSH_ATTR, DEFUNSH_HIDDEN, and DEFUNSH_DEPRECATED so we can
have deprecated commands in vtysh. Similarly, for completeness,
define macros ALIAS_SH, ALIAS_SH_HIDDEN, and ALIAS_SH_DEPRECATED.
Also, fix bug in ALIAS_ATTR macro (didn't matter because it
was never used).
* command.c: Make many functions static instead of global.
(facility_name,facility_match,level_match) New functions
to support enhanced destination-specific logging levels.
(config_write_host) Support new destination-specific logging levels.
(config_logmsg) Added new "logmsg" command to help test logging
system.
(show_logging) Added "show logging" command to show the current
configuration of the logging system.
(config_log_stdout_level) Support explicit stdout logging level.
(no_config_log_stdout) Now takes optional LEVEL arg.
(config_log_monitor,config_log_monitor_level,no_config_log_monitor)
New commands creating new "log monitor" commands to set terminal
monitoring log level.
(config_log_file_level) Support explicit file logging level.
(config_log_syslog_level) Support explicit syslog logging level.
(config_log_facility,no_config_log_facility) Implement new
"log facility" command.
(cmd_init) Add hooks for new commands: "show logging", "logmsg",
"log stdout <level>", "log monitor", "log monitor <level>",
"no log monitor", "log file <filename> <level>",
"no log file <filename> <level>", "log syslog <level>",
"log facility", and "no log facility".
* vty.h: Added a "level" argument to vty_log so it can support
"log record-priority". Declare new function vty_log_fixed for
use in signal handlers.
* vty.c: (vty_log,vty_log_out) Added a "level" argument to support
"log record-priority" for vty terminal monitors.
(vty_down_level) Use config_exit_cmd.func instead of calling
config_exit directly (since command functions will now be static
instead of global).
(vty_log_fixed) New function to send terminal monitor messages
from inside a signal handler.
2004-12-07 16:39:31 +01:00
|
|
|
|
2017-07-17 14:03:14 +02:00
|
|
|
for (i = 0; i < vector_active(vtyvec); i++) {
|
|
|
|
struct vty *vty;
|
|
|
|
if (((vty = vector_slot(vtyvec, i)) != NULL) && vty->monitor)
|
|
|
|
/* N.B. We don't care about the return code, since
|
|
|
|
process is
|
|
|
|
most likely just about to die anyway. */
|
|
|
|
if (writev(vty->wfd, iov, 2) == -1) {
|
|
|
|
fprintf(stderr, "Failure to writev: %d\n",
|
|
|
|
errno);
|
|
|
|
exit(-1);
|
|
|
|
}
|
|
|
|
}
|
2004-12-07 Andrew J. Schorr <ajschorr@alumni.princeton.edu>
* bgp_main.c: (main) The 2nd argument to openzlog has been removed.
* isis_main.c: (main) The 2nd argument to openzlog has been removed.
* ospf6_main.c: (main) The 2nd argument to openzlog has been removed.
Note that stdout logging will no longer be enabled by default when
not running as a daemon.
* ospf_main.c: (main) The 2nd argument to openzlog has been removed.
* rip_main.c: (main) The 2nd argument to openzlog has been removed.
* ripng_main.c: (main) The 2nd argument to openzlog has been removed.
* main.c: (main) The 2nd argument to openzlog has been removed.
So stdout logging will no longer be enabled by default.
* irdp_main.c: (irdp_finish) Reduce severity of shutdown message
from LOG_WARNING to LOG_INFO.
* vtysh.c: Make several functions static instead of global.
Added several commands to support destination-specific logging levels.
(vtysh_completion) This function is unused, so comment it out.
* basic.texi: Document new logging features. Separate basic config
commands from basic VTY commands.
* log.h: Replace struct zlog flags and maskpri fields with maxlvl
array to support individual logging levels for each destination.
Remove the 2nd argument to openzlog since the default logging config
should be standardized inside the library. Replaced the
zlog_set_flag and zlog_reset_flag functions with zlog_set_level.
And zlog_set_file now requires an additional log_level argument.
Declare zlog_proto_names for use inside command.c in the
"show logging" command. Added defines useful for command
construction.
* log.c: (vzlog) Decide where to send the message based on the
individual logging levels configured for each destination.
Remove support for ZLOG_STDERR since it was never actually used.
Support record-priority for terminal monitors.
(zlog_signal,zlog_backtrace_sigsafe) Support destination-specific
logging levels. Remove stderr support (was never used). Added
support for terminal monitor logging.
(_zlog_assert_failed) Increase message severity to LOG_EMERG.
(openzlog) Remove 2nd argument since default config should be
standardized in library. By default, terminal monitoring
is set to debug, and all other logging is disabled.
(zlog_set_flag,zlog_reset_flag) Removed.
(zlog_set_level) New function to replace zlog_set_flag and
zlog_reset_flag. Supports destination-specific logging levels.
(zlog_set_file,zlog_reset_file) Support file-specific logging level.
(zlog_rotate) Log an error message if fopen fails, and support
new file-specific logging level.
* command.h: Change DEFUN_CMD_FUNC_DECL and DEFUN_CMD_FUNC_TEXT so that
command functions will be static instead of global. Remove
declarations for config_exit and config_help. Define new macros
DEFUNSH_ATTR, DEFUNSH_HIDDEN, and DEFUNSH_DEPRECATED so we can
have deprecated commands in vtysh. Similarly, for completeness,
define macros ALIAS_SH, ALIAS_SH_HIDDEN, and ALIAS_SH_DEPRECATED.
Also, fix bug in ALIAS_ATTR macro (didn't matter because it
was never used).
* command.c: Make many functions static instead of global.
(facility_name,facility_match,level_match) New functions
to support enhanced destination-specific logging levels.
(config_write_host) Support new destination-specific logging levels.
(config_logmsg) Added new "logmsg" command to help test logging
system.
(show_logging) Added "show logging" command to show the current
configuration of the logging system.
(config_log_stdout_level) Support explicit stdout logging level.
(no_config_log_stdout) Now takes optional LEVEL arg.
(config_log_monitor,config_log_monitor_level,no_config_log_monitor)
New commands creating new "log monitor" commands to set terminal
monitoring log level.
(config_log_file_level) Support explicit file logging level.
(config_log_syslog_level) Support explicit syslog logging level.
(config_log_facility,no_config_log_facility) Implement new
"log facility" command.
(cmd_init) Add hooks for new commands: "show logging", "logmsg",
"log stdout <level>", "log monitor", "log monitor <level>",
"no log monitor", "log file <filename> <level>",
"no log file <filename> <level>", "log syslog <level>",
"log facility", and "no log facility".
* vty.h: Added a "level" argument to vty_log so it can support
"log record-priority". Declare new function vty_log_fixed for
use in signal handlers.
* vty.c: (vty_log,vty_log_out) Added a "level" argument to support
"log record-priority" for vty terminal monitors.
(vty_down_level) Use config_exit_cmd.func instead of calling
config_exit directly (since command functions will now be static
instead of global).
(vty_log_fixed) New function to send terminal monitor messages
from inside a signal handler.
2004-12-07 16:39:31 +01:00
|
|
|
}
|
|
|
|
|
2017-07-17 14:03:14 +02:00
|
|
|
int vty_config_lock(struct vty *vty)
|
2002-12-13 21:15:29 +01:00
|
|
|
{
|
2017-07-17 14:03:14 +02:00
|
|
|
if (vty_config_is_lockless)
|
|
|
|
return 1;
|
|
|
|
if (vty_config == 0) {
|
|
|
|
vty->config = 1;
|
|
|
|
vty_config = 1;
|
|
|
|
}
|
|
|
|
return vty->config;
|
2002-12-13 21:15:29 +01:00
|
|
|
}
|
|
|
|
|
2017-07-17 14:03:14 +02:00
|
|
|
int vty_config_unlock(struct vty *vty)
|
2002-12-13 21:15:29 +01:00
|
|
|
{
|
2017-07-17 14:03:14 +02:00
|
|
|
if (vty_config_is_lockless)
|
|
|
|
return 0;
|
|
|
|
if (vty_config == 1 && vty->config == 1) {
|
|
|
|
vty->config = 0;
|
|
|
|
vty_config = 0;
|
|
|
|
}
|
|
|
|
return vty->config;
|
2002-12-13 21:15:29 +01:00
|
|
|
}
|
2014-06-04 06:53:35 +02:00
|
|
|
|
2017-07-17 14:03:14 +02:00
|
|
|
void vty_config_lockless(void)
|
2016-09-27 16:53:25 +02:00
|
|
|
{
|
2017-07-17 14:03:14 +02:00
|
|
|
vty_config_is_lockless = 1;
|
2016-09-27 16:53:25 +02:00
|
|
|
}
|
|
|
|
|
2002-12-13 21:15:29 +01:00
|
|
|
/* Master of the threads. */
|
2015-10-13 20:37:15 +02:00
|
|
|
static struct thread_master *vty_master;
|
2002-12-13 21:15:29 +01:00
|
|
|
|
2017-07-17 14:03:14 +02:00
|
|
|
static void vty_event(enum event event, int sock, struct vty *vty)
|
2002-12-13 21:15:29 +01:00
|
|
|
{
|
2017-07-17 14:03:14 +02:00
|
|
|
struct thread *vty_serv_thread = NULL;
|
2017-05-16 17:51:30 +02:00
|
|
|
|
2017-07-17 14:03:14 +02:00
|
|
|
switch (event) {
|
|
|
|
case VTY_SERV:
|
|
|
|
vty_serv_thread = thread_add_read(vty_master, vty_accept, vty,
|
|
|
|
sock, NULL);
|
|
|
|
vector_set_index(Vvty_serv_thread, sock, vty_serv_thread);
|
|
|
|
break;
|
2002-12-13 21:15:29 +01:00
|
|
|
#ifdef VTYSH
|
2017-07-17 14:03:14 +02:00
|
|
|
case VTYSH_SERV:
|
|
|
|
vty_serv_thread = thread_add_read(vty_master, vtysh_accept, vty,
|
|
|
|
sock, NULL);
|
|
|
|
vector_set_index(Vvty_serv_thread, sock, vty_serv_thread);
|
|
|
|
break;
|
|
|
|
case VTYSH_READ:
|
|
|
|
vty->t_read = NULL;
|
|
|
|
thread_add_read(vty_master, vtysh_read, vty, sock,
|
|
|
|
&vty->t_read);
|
|
|
|
break;
|
|
|
|
case VTYSH_WRITE:
|
|
|
|
vty->t_write = NULL;
|
|
|
|
thread_add_write(vty_master, vtysh_write, vty, sock,
|
|
|
|
&vty->t_write);
|
|
|
|
break;
|
2002-12-13 21:15:29 +01:00
|
|
|
#endif /* VTYSH */
|
2017-07-17 14:03:14 +02:00
|
|
|
case VTY_READ:
|
|
|
|
vty->t_read = NULL;
|
|
|
|
thread_add_read(vty_master, vty_read, vty, sock, &vty->t_read);
|
|
|
|
|
|
|
|
/* Time out treatment. */
|
|
|
|
if (vty->v_timeout) {
|
|
|
|
if (vty->t_timeout)
|
|
|
|
thread_cancel(vty->t_timeout);
|
|
|
|
vty->t_timeout = NULL;
|
|
|
|
thread_add_timer(vty_master, vty_timeout, vty,
|
|
|
|
vty->v_timeout, &vty->t_timeout);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case VTY_WRITE:
|
|
|
|
thread_add_write(vty_master, vty_flush, vty, sock,
|
|
|
|
&vty->t_write);
|
|
|
|
break;
|
|
|
|
case VTY_TIMEOUT_RESET:
|
|
|
|
if (vty->t_timeout) {
|
|
|
|
thread_cancel(vty->t_timeout);
|
|
|
|
vty->t_timeout = NULL;
|
|
|
|
}
|
|
|
|
if (vty->v_timeout) {
|
|
|
|
vty->t_timeout = NULL;
|
|
|
|
thread_add_timer(vty_master, vty_timeout, vty,
|
|
|
|
vty->v_timeout, &vty->t_timeout);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
2002-12-13 21:15:29 +01:00
|
|
|
}
|
2014-06-04 06:53:35 +02:00
|
|
|
|
2017-03-22 10:38:22 +01:00
|
|
|
DEFUN_NOSH (config_who,
|
2002-12-13 21:15:29 +01:00
|
|
|
config_who_cmd,
|
|
|
|
"who",
|
|
|
|
"Display who is on vty\n")
|
|
|
|
{
|
2017-07-17 14:03:14 +02:00
|
|
|
unsigned int i;
|
|
|
|
struct vty *v;
|
2002-12-13 21:15:29 +01:00
|
|
|
|
2017-07-17 14:03:14 +02:00
|
|
|
for (i = 0; i < vector_active(vtyvec); i++)
|
|
|
|
if ((v = vector_slot(vtyvec, i)) != NULL)
|
|
|
|
vty_out(vty, "%svty[%d] connected from %s.\n",
|
|
|
|
v->config ? "*" : " ", i, v->address);
|
|
|
|
return CMD_SUCCESS;
|
2002-12-13 21:15:29 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Move to vty configuration mode. */
|
2017-03-22 10:38:22 +01:00
|
|
|
DEFUN_NOSH (line_vty,
|
2002-12-13 21:15:29 +01:00
|
|
|
line_vty_cmd,
|
|
|
|
"line vty",
|
|
|
|
"Configure a terminal line\n"
|
|
|
|
"Virtual terminal\n")
|
|
|
|
{
|
2017-07-17 14:03:14 +02:00
|
|
|
vty->node = VTY_NODE;
|
|
|
|
return CMD_SUCCESS;
|
2002-12-13 21:15:29 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Set time out value. */
|
2017-07-17 14:03:14 +02:00
|
|
|
static int exec_timeout(struct vty *vty, const char *min_str,
|
|
|
|
const char *sec_str)
|
2002-12-13 21:15:29 +01:00
|
|
|
{
|
2017-07-17 14:03:14 +02:00
|
|
|
unsigned long timeout = 0;
|
2002-12-13 21:15:29 +01:00
|
|
|
|
2017-07-17 14:03:14 +02:00
|
|
|
/* min_str and sec_str are already checked by parser. So it must be
|
|
|
|
all digit string. */
|
|
|
|
if (min_str) {
|
|
|
|
timeout = strtol(min_str, NULL, 10);
|
|
|
|
timeout *= 60;
|
|
|
|
}
|
|
|
|
if (sec_str)
|
|
|
|
timeout += strtol(sec_str, NULL, 10);
|
2002-12-13 21:15:29 +01:00
|
|
|
|
2017-07-17 14:03:14 +02:00
|
|
|
vty_timeout_val = timeout;
|
|
|
|
vty->v_timeout = timeout;
|
|
|
|
vty_event(VTY_TIMEOUT_RESET, 0, vty);
|
2002-12-13 21:15:29 +01:00
|
|
|
|
|
|
|
|
2017-07-17 14:03:14 +02:00
|
|
|
return CMD_SUCCESS;
|
2002-12-13 21:15:29 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
DEFUN (exec_timeout_min,
|
|
|
|
exec_timeout_min_cmd,
|
2016-09-22 22:17:48 +02:00
|
|
|
"exec-timeout (0-35791)",
|
2002-12-13 21:15:29 +01:00
|
|
|
"Set timeout value\n"
|
|
|
|
"Timeout value in minutes\n")
|
|
|
|
{
|
2017-07-17 14:03:14 +02:00
|
|
|
int idx_number = 1;
|
|
|
|
return exec_timeout(vty, argv[idx_number]->arg, NULL);
|
2002-12-13 21:15:29 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
DEFUN (exec_timeout_sec,
|
|
|
|
exec_timeout_sec_cmd,
|
2016-09-22 22:17:48 +02:00
|
|
|
"exec-timeout (0-35791) (0-2147483)",
|
2002-12-13 21:15:29 +01:00
|
|
|
"Set the EXEC timeout\n"
|
|
|
|
"Timeout in minutes\n"
|
|
|
|
"Timeout in seconds\n")
|
|
|
|
{
|
2017-07-17 14:03:14 +02:00
|
|
|
int idx_number = 1;
|
|
|
|
int idx_number_2 = 2;
|
|
|
|
return exec_timeout(vty, argv[idx_number]->arg,
|
|
|
|
argv[idx_number_2]->arg);
|
2002-12-13 21:15:29 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
DEFUN (no_exec_timeout,
|
|
|
|
no_exec_timeout_cmd,
|
|
|
|
"no exec-timeout",
|
|
|
|
NO_STR
|
|
|
|
"Set the EXEC timeout\n")
|
|
|
|
{
|
2017-07-17 14:03:14 +02:00
|
|
|
return exec_timeout(vty, NULL, NULL);
|
2002-12-13 21:15:29 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Set vty access class. */
|
|
|
|
DEFUN (vty_access_class,
|
|
|
|
vty_access_class_cmd,
|
|
|
|
"access-class WORD",
|
|
|
|
"Filter connections based on an IP access list\n"
|
|
|
|
"IP access list\n")
|
|
|
|
{
|
2017-07-17 14:03:14 +02:00
|
|
|
int idx_word = 1;
|
|
|
|
if (vty_accesslist_name)
|
|
|
|
XFREE(MTYPE_VTY, vty_accesslist_name);
|
2002-12-13 21:15:29 +01:00
|
|
|
|
2017-07-17 14:03:14 +02:00
|
|
|
vty_accesslist_name = XSTRDUP(MTYPE_VTY, argv[idx_word]->arg);
|
2002-12-13 21:15:29 +01:00
|
|
|
|
2017-07-17 14:03:14 +02:00
|
|
|
return CMD_SUCCESS;
|
2002-12-13 21:15:29 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Clear vty access class. */
|
|
|
|
DEFUN (no_vty_access_class,
|
|
|
|
no_vty_access_class_cmd,
|
|
|
|
"no access-class [WORD]",
|
|
|
|
NO_STR
|
|
|
|
"Filter connections based on an IP access list\n"
|
|
|
|
"IP access list\n")
|
|
|
|
{
|
2017-07-17 14:03:14 +02:00
|
|
|
int idx_word = 2;
|
|
|
|
const char *accesslist = (argc == 3) ? argv[idx_word]->arg : NULL;
|
|
|
|
if (!vty_accesslist_name
|
|
|
|
|| (argc == 3 && strcmp(vty_accesslist_name, accesslist))) {
|
|
|
|
vty_out(vty, "Access-class is not currently applied to vty\n");
|
|
|
|
return CMD_WARNING_CONFIG_FAILED;
|
|
|
|
}
|
2002-12-13 21:15:29 +01:00
|
|
|
|
2017-07-17 14:03:14 +02:00
|
|
|
XFREE(MTYPE_VTY, vty_accesslist_name);
|
2002-12-13 21:15:29 +01:00
|
|
|
|
2017-07-17 14:03:14 +02:00
|
|
|
vty_accesslist_name = NULL;
|
2002-12-13 21:15:29 +01:00
|
|
|
|
2017-07-17 14:03:14 +02:00
|
|
|
return CMD_SUCCESS;
|
2002-12-13 21:15:29 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Set vty access class. */
|
|
|
|
DEFUN (vty_ipv6_access_class,
|
|
|
|
vty_ipv6_access_class_cmd,
|
|
|
|
"ipv6 access-class WORD",
|
|
|
|
IPV6_STR
|
|
|
|
"Filter connections based on an IP access list\n"
|
|
|
|
"IPv6 access list\n")
|
|
|
|
{
|
2017-07-17 14:03:14 +02:00
|
|
|
int idx_word = 2;
|
|
|
|
if (vty_ipv6_accesslist_name)
|
|
|
|
XFREE(MTYPE_VTY, vty_ipv6_accesslist_name);
|
2002-12-13 21:15:29 +01:00
|
|
|
|
2017-07-17 14:03:14 +02:00
|
|
|
vty_ipv6_accesslist_name = XSTRDUP(MTYPE_VTY, argv[idx_word]->arg);
|
2002-12-13 21:15:29 +01:00
|
|
|
|
2017-07-17 14:03:14 +02:00
|
|
|
return CMD_SUCCESS;
|
2002-12-13 21:15:29 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Clear vty access class. */
|
|
|
|
DEFUN (no_vty_ipv6_access_class,
|
|
|
|
no_vty_ipv6_access_class_cmd,
|
|
|
|
"no ipv6 access-class [WORD]",
|
|
|
|
NO_STR
|
|
|
|
IPV6_STR
|
|
|
|
"Filter connections based on an IP access list\n"
|
|
|
|
"IPv6 access list\n")
|
|
|
|
{
|
2017-07-17 14:03:14 +02:00
|
|
|
int idx_word = 3;
|
|
|
|
const char *accesslist = (argc == 4) ? argv[idx_word]->arg : NULL;
|
2016-09-22 22:17:48 +02:00
|
|
|
|
2017-07-17 14:03:14 +02:00
|
|
|
if (!vty_ipv6_accesslist_name
|
|
|
|
|| (argc == 4 && strcmp(vty_ipv6_accesslist_name, accesslist))) {
|
|
|
|
vty_out(vty,
|
|
|
|
"IPv6 access-class is not currently applied to vty\n");
|
|
|
|
return CMD_WARNING_CONFIG_FAILED;
|
|
|
|
}
|
2002-12-13 21:15:29 +01:00
|
|
|
|
2017-07-17 14:03:14 +02:00
|
|
|
XFREE(MTYPE_VTY, vty_ipv6_accesslist_name);
|
2002-12-13 21:15:29 +01:00
|
|
|
|
2017-07-17 14:03:14 +02:00
|
|
|
vty_ipv6_accesslist_name = NULL;
|
2002-12-13 21:15:29 +01:00
|
|
|
|
2017-07-17 14:03:14 +02:00
|
|
|
return CMD_SUCCESS;
|
2002-12-13 21:15:29 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/* vty login. */
|
|
|
|
DEFUN (vty_login,
|
|
|
|
vty_login_cmd,
|
|
|
|
"login",
|
|
|
|
"Enable password checking\n")
|
|
|
|
{
|
2017-07-17 14:03:14 +02:00
|
|
|
no_password_check = 0;
|
|
|
|
return CMD_SUCCESS;
|
2002-12-13 21:15:29 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
DEFUN (no_vty_login,
|
|
|
|
no_vty_login_cmd,
|
|
|
|
"no login",
|
|
|
|
NO_STR
|
|
|
|
"Enable password checking\n")
|
|
|
|
{
|
2017-07-17 14:03:14 +02:00
|
|
|
no_password_check = 1;
|
|
|
|
return CMD_SUCCESS;
|
2002-12-13 21:15:29 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
DEFUN (service_advanced_vty,
|
|
|
|
service_advanced_vty_cmd,
|
|
|
|
"service advanced-vty",
|
|
|
|
"Set up miscellaneous service\n"
|
|
|
|
"Enable advanced mode vty interface\n")
|
|
|
|
{
|
2017-07-17 14:03:14 +02:00
|
|
|
host.advanced = 1;
|
|
|
|
return CMD_SUCCESS;
|
2002-12-13 21:15:29 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
DEFUN (no_service_advanced_vty,
|
|
|
|
no_service_advanced_vty_cmd,
|
|
|
|
"no service advanced-vty",
|
|
|
|
NO_STR
|
|
|
|
"Set up miscellaneous service\n"
|
|
|
|
"Enable advanced mode vty interface\n")
|
|
|
|
{
|
2017-07-17 14:03:14 +02:00
|
|
|
host.advanced = 0;
|
|
|
|
return CMD_SUCCESS;
|
2002-12-13 21:15:29 +01:00
|
|
|
}
|
|
|
|
|
2017-03-22 10:38:22 +01:00
|
|
|
DEFUN_NOSH (terminal_monitor,
|
2002-12-13 21:15:29 +01:00
|
|
|
terminal_monitor_cmd,
|
|
|
|
"terminal monitor",
|
|
|
|
"Set terminal line parameters\n"
|
|
|
|
"Copy debug output to the current terminal line\n")
|
|
|
|
{
|
2017-07-17 14:03:14 +02:00
|
|
|
vty->monitor = 1;
|
|
|
|
return CMD_SUCCESS;
|
2002-12-13 21:15:29 +01:00
|
|
|
}
|
|
|
|
|
2017-03-22 10:38:22 +01:00
|
|
|
DEFUN_NOSH (terminal_no_monitor,
|
2002-12-13 21:15:29 +01:00
|
|
|
terminal_no_monitor_cmd,
|
|
|
|
"terminal no monitor",
|
|
|
|
"Set terminal line parameters\n"
|
|
|
|
NO_STR
|
|
|
|
"Copy debug output to the current terminal line\n")
|
|
|
|
{
|
2017-07-17 14:03:14 +02:00
|
|
|
vty->monitor = 0;
|
|
|
|
return CMD_SUCCESS;
|
2002-12-13 21:15:29 +01:00
|
|
|
}
|
|
|
|
|
2017-03-22 10:38:22 +01:00
|
|
|
DEFUN_NOSH (no_terminal_monitor,
|
2006-01-17 18:42:03 +01:00
|
|
|
no_terminal_monitor_cmd,
|
|
|
|
"no terminal monitor",
|
|
|
|
NO_STR
|
|
|
|
"Set terminal line parameters\n"
|
|
|
|
"Copy debug output to the current terminal line\n")
|
2016-09-23 22:08:47 +02:00
|
|
|
{
|
2017-07-17 14:03:14 +02:00
|
|
|
return terminal_no_monitor(self, vty, argc, argv);
|
2016-09-23 22:08:47 +02:00
|
|
|
}
|
|
|
|
|
2006-01-17 18:42:03 +01:00
|
|
|
|
2017-03-22 10:38:22 +01:00
|
|
|
DEFUN_NOSH (show_history,
|
2002-12-13 21:15:29 +01:00
|
|
|
show_history_cmd,
|
|
|
|
"show history",
|
|
|
|
SHOW_STR
|
|
|
|
"Display the session command history\n")
|
|
|
|
{
|
2017-07-17 14:03:14 +02:00
|
|
|
int index;
|
2002-12-13 21:15:29 +01:00
|
|
|
|
2017-07-17 14:03:14 +02:00
|
|
|
for (index = vty->hindex + 1; index != vty->hindex;) {
|
|
|
|
if (index == VTY_MAXHIST) {
|
|
|
|
index = 0;
|
|
|
|
continue;
|
|
|
|
}
|
2002-12-13 21:15:29 +01:00
|
|
|
|
2017-07-17 14:03:14 +02:00
|
|
|
if (vty->hist[index] != NULL)
|
|
|
|
vty_out(vty, " %s\n", vty->hist[index]);
|
2002-12-13 21:15:29 +01:00
|
|
|
|
2017-07-17 14:03:14 +02:00
|
|
|
index++;
|
|
|
|
}
|
2002-12-13 21:15:29 +01:00
|
|
|
|
2017-07-17 14:03:14 +02:00
|
|
|
return CMD_SUCCESS;
|
2002-12-13 21:15:29 +01:00
|
|
|
}
|
|
|
|
|
2016-05-17 18:19:51 +02:00
|
|
|
/* vty login. */
|
|
|
|
DEFUN (log_commands,
|
|
|
|
log_commands_cmd,
|
|
|
|
"log commands",
|
|
|
|
"Logging control\n"
|
|
|
|
"Log all commands (can't be unset without restart)\n")
|
|
|
|
{
|
2017-07-17 14:03:14 +02:00
|
|
|
do_log_commands = 1;
|
|
|
|
return CMD_SUCCESS;
|
2016-05-17 18:19:51 +02:00
|
|
|
}
|
|
|
|
|
2002-12-13 21:15:29 +01:00
|
|
|
/* Display current configuration. */
|
2017-07-17 14:03:14 +02:00
|
|
|
static int vty_config_write(struct vty *vty)
|
2002-12-13 21:15:29 +01:00
|
|
|
{
|
2017-07-17 14:03:14 +02:00
|
|
|
vty_out(vty, "line vty\n");
|
2002-12-13 21:15:29 +01:00
|
|
|
|
2017-07-17 14:03:14 +02:00
|
|
|
if (vty_accesslist_name)
|
|
|
|
vty_out(vty, " access-class %s\n", vty_accesslist_name);
|
2002-12-13 21:15:29 +01:00
|
|
|
|
2017-07-17 14:03:14 +02:00
|
|
|
if (vty_ipv6_accesslist_name)
|
|
|
|
vty_out(vty, " ipv6 access-class %s\n",
|
|
|
|
vty_ipv6_accesslist_name);
|
2002-12-13 21:15:29 +01:00
|
|
|
|
2017-07-17 14:03:14 +02:00
|
|
|
/* exec-timeout */
|
|
|
|
if (vty_timeout_val != VTY_TIMEOUT_DEFAULT)
|
|
|
|
vty_out(vty, " exec-timeout %ld %ld\n", vty_timeout_val / 60,
|
|
|
|
vty_timeout_val % 60);
|
2002-12-13 21:15:29 +01:00
|
|
|
|
2017-07-17 14:03:14 +02:00
|
|
|
/* login */
|
|
|
|
if (no_password_check)
|
|
|
|
vty_out(vty, " no login\n");
|
2016-05-17 18:19:51 +02:00
|
|
|
|
2017-07-17 14:03:14 +02:00
|
|
|
if (do_log_commands)
|
|
|
|
vty_out(vty, "log commands\n");
|
2016-09-20 01:46:51 +02:00
|
|
|
|
2017-07-17 14:03:14 +02:00
|
|
|
vty_out(vty, "!\n");
|
2002-12-13 21:15:29 +01:00
|
|
|
|
2017-07-17 14:03:14 +02:00
|
|
|
return CMD_SUCCESS;
|
2002-12-13 21:15:29 +01:00
|
|
|
}
|
|
|
|
|
2017-07-17 14:03:14 +02:00
|
|
|
struct cmd_node vty_node = {
|
2017-07-22 14:52:33 +02:00
|
|
|
VTY_NODE, "%s(config-line)# ", 1,
|
2002-12-13 21:15:29 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
/* Reset all VTY status. */
|
2017-07-17 14:03:14 +02:00
|
|
|
void vty_reset()
|
|
|
|
{
|
|
|
|
unsigned int i;
|
|
|
|
struct vty *vty;
|
|
|
|
struct thread *vty_serv_thread;
|
|
|
|
|
|
|
|
for (i = 0; i < vector_active(vtyvec); i++)
|
|
|
|
if ((vty = vector_slot(vtyvec, i)) != NULL) {
|
2018-06-06 21:09:44 +02:00
|
|
|
buffer_reset(vty->lbuf);
|
2017-07-17 14:03:14 +02:00
|
|
|
buffer_reset(vty->obuf);
|
|
|
|
vty->status = VTY_CLOSE;
|
|
|
|
vty_close(vty);
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 0; i < vector_active(Vvty_serv_thread); i++)
|
|
|
|
if ((vty_serv_thread = vector_slot(Vvty_serv_thread, i))
|
|
|
|
!= NULL) {
|
|
|
|
thread_cancel(vty_serv_thread);
|
|
|
|
vector_slot(Vvty_serv_thread, i) = NULL;
|
|
|
|
close(i);
|
|
|
|
}
|
|
|
|
|
|
|
|
vty_timeout_val = VTY_TIMEOUT_DEFAULT;
|
|
|
|
|
|
|
|
if (vty_accesslist_name) {
|
|
|
|
XFREE(MTYPE_VTY, vty_accesslist_name);
|
|
|
|
vty_accesslist_name = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (vty_ipv6_accesslist_name) {
|
|
|
|
XFREE(MTYPE_VTY, vty_ipv6_accesslist_name);
|
|
|
|
vty_ipv6_accesslist_name = NULL;
|
|
|
|
}
|
2002-12-13 21:15:29 +01:00
|
|
|
}
|
|
|
|
|
2017-07-17 14:03:14 +02:00
|
|
|
static void vty_save_cwd(void)
|
2002-12-13 21:15:29 +01:00
|
|
|
{
|
2017-07-17 14:03:14 +02:00
|
|
|
char cwd[MAXPATHLEN];
|
|
|
|
char *c;
|
2003-10-16 00:09:28 +02:00
|
|
|
|
2017-07-17 14:03:14 +02:00
|
|
|
c = getcwd(cwd, MAXPATHLEN);
|
2002-12-13 21:15:29 +01:00
|
|
|
|
2017-07-17 14:03:14 +02:00
|
|
|
if (!c) {
|
|
|
|
/*
|
|
|
|
* At this point if these go wrong, more than likely
|
|
|
|
* the whole world is coming down around us
|
|
|
|
* Hence not worrying about it too much.
|
|
|
|
*/
|
|
|
|
if (!chdir(SYSCONFDIR)) {
|
2018-06-14 15:13:18 +02:00
|
|
|
zlog_ferr(LIB_ERR_SYSTEM_CALL,
|
|
|
|
"Failure to chdir to %s, errno: %d",
|
|
|
|
SYSCONFDIR, errno);
|
2017-07-17 14:03:14 +02:00
|
|
|
exit(-1);
|
|
|
|
}
|
|
|
|
if (getcwd(cwd, MAXPATHLEN) == NULL) {
|
2018-06-14 15:13:18 +02:00
|
|
|
zlog_ferr(LIB_ERR_SYSTEM_CALL,
|
|
|
|
"Failure to getcwd, errno: %d", errno);
|
2017-07-17 14:03:14 +02:00
|
|
|
exit(-1);
|
|
|
|
}
|
|
|
|
}
|
2002-12-13 21:15:29 +01:00
|
|
|
|
2017-07-17 14:03:14 +02:00
|
|
|
vty_cwd = XMALLOC(MTYPE_TMP, strlen(cwd) + 1);
|
|
|
|
strcpy(vty_cwd, cwd);
|
2002-12-13 21:15:29 +01:00
|
|
|
}
|
|
|
|
|
2017-07-17 14:03:14 +02:00
|
|
|
char *vty_get_cwd()
|
2002-12-13 21:15:29 +01:00
|
|
|
{
|
2017-07-17 14:03:14 +02:00
|
|
|
return vty_cwd;
|
2002-12-13 21:15:29 +01:00
|
|
|
}
|
|
|
|
|
2017-07-17 14:03:14 +02:00
|
|
|
int vty_shell(struct vty *vty)
|
2002-12-13 21:15:29 +01:00
|
|
|
{
|
2017-07-17 14:03:14 +02:00
|
|
|
return vty->type == VTY_SHELL ? 1 : 0;
|
2002-12-13 21:15:29 +01:00
|
|
|
}
|
|
|
|
|
2017-07-17 14:03:14 +02:00
|
|
|
int vty_shell_serv(struct vty *vty)
|
2002-12-13 21:15:29 +01:00
|
|
|
{
|
2017-07-17 14:03:14 +02:00
|
|
|
return vty->type == VTY_SHELL_SERV ? 1 : 0;
|
2002-12-13 21:15:29 +01:00
|
|
|
}
|
|
|
|
|
2017-07-17 14:03:14 +02:00
|
|
|
void vty_init_vtysh()
|
2002-12-13 21:15:29 +01:00
|
|
|
{
|
2017-07-17 14:03:14 +02:00
|
|
|
vtyvec = vector_init(VECTOR_MIN_SIZE);
|
2002-12-13 21:15:29 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Install vty's own commands like `who' command. */
|
2017-07-17 14:03:14 +02:00
|
|
|
void vty_init(struct thread_master *master_thread)
|
|
|
|
{
|
|
|
|
/* For further configuration read, preserve current directory. */
|
|
|
|
vty_save_cwd();
|
|
|
|
|
|
|
|
vtyvec = vector_init(VECTOR_MIN_SIZE);
|
|
|
|
|
|
|
|
vty_master = master_thread;
|
|
|
|
|
2017-06-01 16:02:23 +02:00
|
|
|
atexit(vty_stdio_atexit);
|
2017-07-17 14:03:14 +02:00
|
|
|
|
|
|
|
/* Initilize server thread vector. */
|
|
|
|
Vvty_serv_thread = vector_init(VECTOR_MIN_SIZE);
|
|
|
|
|
|
|
|
/* Install bgp top node. */
|
|
|
|
install_node(&vty_node, vty_config_write);
|
|
|
|
|
|
|
|
install_element(VIEW_NODE, &config_who_cmd);
|
|
|
|
install_element(VIEW_NODE, &show_history_cmd);
|
|
|
|
install_element(CONFIG_NODE, &line_vty_cmd);
|
|
|
|
install_element(CONFIG_NODE, &service_advanced_vty_cmd);
|
|
|
|
install_element(CONFIG_NODE, &no_service_advanced_vty_cmd);
|
|
|
|
install_element(CONFIG_NODE, &show_history_cmd);
|
|
|
|
install_element(CONFIG_NODE, &log_commands_cmd);
|
|
|
|
install_element(ENABLE_NODE, &terminal_monitor_cmd);
|
|
|
|
install_element(ENABLE_NODE, &terminal_no_monitor_cmd);
|
|
|
|
install_element(ENABLE_NODE, &no_terminal_monitor_cmd);
|
|
|
|
|
|
|
|
install_default(VTY_NODE);
|
|
|
|
install_element(VTY_NODE, &exec_timeout_min_cmd);
|
|
|
|
install_element(VTY_NODE, &exec_timeout_sec_cmd);
|
|
|
|
install_element(VTY_NODE, &no_exec_timeout_cmd);
|
|
|
|
install_element(VTY_NODE, &vty_access_class_cmd);
|
|
|
|
install_element(VTY_NODE, &no_vty_access_class_cmd);
|
|
|
|
install_element(VTY_NODE, &vty_login_cmd);
|
|
|
|
install_element(VTY_NODE, &no_vty_login_cmd);
|
|
|
|
install_element(VTY_NODE, &vty_ipv6_access_class_cmd);
|
|
|
|
install_element(VTY_NODE, &no_vty_ipv6_access_class_cmd);
|
|
|
|
}
|
|
|
|
|
|
|
|
void vty_terminate(void)
|
|
|
|
{
|
|
|
|
if (vty_cwd)
|
|
|
|
XFREE(MTYPE_TMP, vty_cwd);
|
|
|
|
|
|
|
|
if (vtyvec && Vvty_serv_thread) {
|
|
|
|
vty_reset();
|
|
|
|
vector_free(vtyvec);
|
|
|
|
vector_free(Vvty_serv_thread);
|
|
|
|
vtyvec = NULL;
|
|
|
|
Vvty_serv_thread = NULL;
|
|
|
|
}
|
[bgpd] Stability fixes including bugs 397, 492
I've spent the last several weeks working on stability fixes to bgpd.
These patches fix all of the numerous crashes, assertion failures, memory
leaks and memory stomping I could find. Valgrind was used extensively.
Added new function bgp_exit() to help catch problems. If "debug bgp" is
configured and bgpd exits with status of 0, statistics on remaining
lib/memory.c allocations are printed to stderr. It is my hope that other
developers will use this to stay on top of memory issues.
Example questionable exit:
bgpd: memstats: Current memory utilization in module LIB:
bgpd: memstats: Link List : 6
bgpd: memstats: Link Node : 5
bgpd: memstats: Hash : 8
bgpd: memstats: Hash Bucket : 2
bgpd: memstats: Hash Index : 8
bgpd: memstats: Work queue : 3
bgpd: memstats: Work queue item : 2
bgpd: memstats: Work queue name string : 3
bgpd: memstats: Current memory utilization in module BGP:
bgpd: memstats: BGP instance : 1
bgpd: memstats: BGP peer : 1
bgpd: memstats: BGP peer hostname : 1
bgpd: memstats: BGP attribute : 1
bgpd: memstats: BGP extra attributes : 1
bgpd: memstats: BGP aspath : 1
bgpd: memstats: BGP aspath str : 1
bgpd: memstats: BGP table : 24
bgpd: memstats: BGP node : 1
bgpd: memstats: BGP route : 1
bgpd: memstats: BGP synchronise : 8
bgpd: memstats: BGP Process queue : 1
bgpd: memstats: BGP node clear queue : 1
bgpd: memstats: NOTE: If configuration exists, utilization may be expected.
Example clean exit:
bgpd: memstats: No remaining tracked memory utilization.
This patch fixes bug #397: "Invalid free in bgp_announce_check()".
This patch fixes bug #492: "SIGBUS in bgpd/bgp_route.c:
bgp_clear_route_node()".
My apologies for not separating out these changes into individual patches.
The complexity of doing so boggled what is left of my brain. I hope this
is all still useful to the community.
This code has been production tested, in non-route-server-client mode, on
a linux 32-bit box and a 64-bit box.
Release/reset functions, used by bgp_exit(), added to:
bgpd/bgp_attr.c,h
bgpd/bgp_community.c,h
bgpd/bgp_dump.c,h
bgpd/bgp_ecommunity.c,h
bgpd/bgp_filter.c,h
bgpd/bgp_nexthop.c,h
bgpd/bgp_route.c,h
lib/routemap.c,h
File by file analysis:
* bgpd/bgp_aspath.c: Prevent re-use of ashash after it is released.
* bgpd/bgp_attr.c: #if removed uncalled cluster_dup().
* bgpd/bgp_clist.c,h: Allow community_list_terminate() to be called from
bgp_exit().
* bgpd/bgp_filter.c: Fix aslist->name use without allocation check, and
also fix memory leak.
* bgpd/bgp_main.c: Created bgp_exit() exit routine. This function frees
allocations made as part of bgpd initialization and, to some extent,
configuration. If "debug bgp" is configured, memory stats are printed
as described above.
* bgpd/bgp_nexthop.c: zclient_new() already allocates stream for
ibuf/obuf, so bgp_scan_init() shouldn't do it too. Also, made it so
zlookup is global so bgp_exit() can use it.
* bgpd/bgp_packet.c: bgp_capability_msg_parse() call to bgp_clear_route()
adjusted to use new BGP_CLEAR_ROUTE_NORMAL flag.
* bgpd/bgp_route.h: Correct reference counter "lock" to be signed.
bgp_clear_route() now accepts a bgp_clear_route_type of either
BGP_CLEAR_ROUTE_NORMAL or BGP_CLEAR_ROUTE_MY_RSCLIENT.
* bgpd/bgp_route.c:
- bgp_process_rsclient(): attr was being zero'ed and then
bgp_attr_extra_free() was being called with it, even though it was
never filled with valid data.
- bgp_process_rsclient(): Make sure rsclient->group is not NULL before
use.
- bgp_processq_del(): Add call to bgp_table_unlock().
- bgp_process(): Add call to bgp_table_lock().
- bgp_update_rsclient(): memset clearing of new_attr not needed since
declarationw with "= { 0 }" does it. memset was already commented
out.
- bgp_update_rsclient(): Fix screwed up misleading indentation.
- bgp_withdraw_rsclient(): Fix screwed up misleading indentation.
- bgp_clear_route_node(): Support BGP_CLEAR_ROUTE_MY_RSCLIENT.
- bgp_clear_node_queue_del(): Add call to bgp_table_unlock() and also
free struct bgp_clear_node_queue used for work item.
- bgp_clear_node_complete(): Do peer_unlock() after BGP_EVENT_ADD() in
case peer is released by peer_unlock() call.
- bgp_clear_route_table(): Support BGP_CLEAR_ROUTE_MY_RSCLIENT. Use
struct bgp_clear_node_queue to supply data to worker. Add call to
bgp_table_lock().
- bgp_clear_route(): Add support for BGP_CLEAR_ROUTE_NORMAL or
BGP_CLEAR_ROUTE_MY_RSCLIENT.
- bgp_clear_route_all(): Use BGP_CLEAR_ROUTE_NORMAL.
Bug 397 fixes:
- bgp_default_originate()
- bgp_announce_table()
* bgpd/bgp_table.h:
- struct bgp_table: Added reference count. Changed type of owner to be
"struct peer *" rather than "void *".
- struct bgp_node: Correct reference counter "lock" to be signed.
* bgpd/bgp_table.c:
- Added bgp_table reference counting.
- bgp_table_free(): Fixed cleanup code. Call peer_unlock() on owner if
set.
- bgp_unlock_node(): Added assertion.
- bgp_node_get(): Added call to bgp_lock_node() to code path that it was
missing from.
* bgpd/bgp_vty.c:
- peer_rsclient_set_vty(): Call peer_lock() as part of peer assignment
to owner. Handle failure gracefully.
- peer_rsclient_unset_vty(): Add call to bgp_clear_route() with
BGP_CLEAR_ROUTE_MY_RSCLIENT purpose.
* bgpd/bgp_zebra.c: Made it so zclient is global so bgp_exit() can use it.
* bgpd/bgpd.c:
- peer_lock(): Allow to be called when status is "Deleted".
- peer_deactivate(): Supply BGP_CLEAR_ROUTE_NORMAL purpose to
bgp_clear_route() call.
- peer_delete(): Common variable listnode pn. Fix bug in which rsclient
was only dealt with if not part of a peer group. Call
bgp_clear_route() for rsclient, if appropriate, and do so with
BGP_CLEAR_ROUTE_MY_RSCLIENT purpose.
- peer_group_get(): Use XSTRDUP() instead of strdup() for conf->host.
- peer_group_bind(): Call bgp_clear_route() for rsclient, and do so with
BGP_CLEAR_ROUTE_MY_RSCLIENT purpose.
- bgp_create(): Use XSTRDUP() instead of strdup() for peer_self->host.
- bgp_delete(): Delete peers before groups, rather than after. And then
rather than deleting rsclients, verify that there are none at this
point.
- bgp_unlock(): Add assertion.
- bgp_free(): Call bgp_table_finish() rather than doing XFREE() itself.
* lib/command.c,h: Compiler warning fixes. Add cmd_terminate(). Fixed
massive leak in install_element() in which cmd_make_descvec() was being
called more than once for the same cmd->strvec/string/doc.
* lib/log.c: Make closezlog() check fp before calling fclose().
* lib/memory.c: Catch when alloc count goes negative by using signed
counts. Correct #endif comment. Add log_memstats_stderr().
* lib/memory.h: Add log_memstats_stderr().
* lib/thread.c: thread->funcname was being accessed in thread_call() after
it had been freed. Rearranged things so that thread_call() frees
funcname. Also made it so thread_master_free() cleans up cpu_record.
* lib/vty.c,h: Use global command_cr. Add vty_terminate().
* lib/zclient.c,h: Re-enable zclient_free().
2009-07-18 07:44:03 +02:00
|
|
|
}
|