diff --git a/kronosnetd/Makefile.am b/kronosnetd/Makefile.am index d2520255..f99af09b 100644 --- a/kronosnetd/Makefile.am +++ b/kronosnetd/Makefile.am @@ -1,22 +1,22 @@ MAINTAINERCLEANFILES = Makefile.in noinst_HEADERS = \ cfg.h \ - utils.h + logging.h sbin_PROGRAMS = kronosnetd kronosnetd_SOURCES = \ cfg.c \ main.c \ - utils.c + logging.c kronosnetd_CPPFLAGS = \ -I$(top_srcdir)/libtap \ -I$(top_srcdir)/libknet \ -I$(top_srcdir)/libvty kronosnetd_LDADD = \ $(top_builddir)/libvty/libvty.a \ $(top_builddir)/libknet/libknet.a \ $(top_builddir)/libtap/libtap.la diff --git a/kronosnetd/utils.c b/kronosnetd/logging.c similarity index 52% rename from kronosnetd/utils.c rename to kronosnetd/logging.c index 1db4aac9..73b37567 100644 --- a/kronosnetd/utils.c +++ b/kronosnetd/logging.c @@ -1,9 +1,6 @@ #include "config.h" -#include -#include - -#include "utils.h" +#include "logging.h" int utils_debug = 0; int utils_syslog = 0; diff --git a/kronosnetd/utils.h b/kronosnetd/logging.h similarity index 100% rename from kronosnetd/utils.h rename to kronosnetd/logging.h diff --git a/kronosnetd/main.c b/kronosnetd/main.c index 74dabd5e..15835622 100644 --- a/kronosnetd/main.c +++ b/kronosnetd/main.c @@ -1,318 +1,318 @@ #include "config.h" #include #include #include #include #include #include #include #include #include #include "cfg.h" #include "vty.h" -#include "utils.h" +#include "logging.h" #define LOCKFILE_NAME RUNDIR PACKAGE ".pid" #define OPTION_STRING "hdfVc:l:b:p:" static int daemonize = 1; struct knet_cfg_top knet_cfg_head; extern int utils_debug; static void print_usage(void) { printf("Usage:\n\n"); printf(PACKAGE " [options]\n\n"); printf("Options:\n\n"); printf(" -b Bind management VTY to ip_addr (default: all)\n"); printf(" -p Bind management VTY to port (default %d)\n", KNET_VTY_DEFAULT_PORT); printf(" -c Use config file (default "DEFAULT_CONFIG_FILE")\n"); printf(" -l Use log file (default "DEFAULT_LOG_FILE")\n"); printf(" -f Do not fork in background\n"); printf(" -d Enable debugging output\n"); printf(" -h This help\n"); printf(" -V Print program version information\n"); return; } static int read_arguments(int argc, char **argv) { int cont = 1; int optchar; int int_port; while (cont) { optchar = getopt(argc, argv, OPTION_STRING); switch (optchar) { case 'b': knet_cfg_head.vty_ip = strdup(optarg); if (!knet_cfg_head.vty_ip) return -1; break; case 'p': int_port = atoi(optarg); if ((int_port < 0) || (int_port > 65535)) { errno = EINVAL; return -1; } knet_cfg_head.vty_port = strdup(optarg); if (!knet_cfg_head.vty_port) return -1; break; case 'c': knet_cfg_head.conffile = strdup(optarg); if (!knet_cfg_head.conffile) return -1; break; case 'l': knet_cfg_head.logfile = strdup(optarg); if (!knet_cfg_head.logfile) return -1; break; case 'd': utils_debug = 1; break; case 'f': daemonize = 0; break; case 'h': print_usage(); exit(EXIT_SUCCESS); break; case 'V': printf(PACKAGE " " PACKAGE_VERSION " (built " __DATE__ " " __TIME__ ")\n"); exit(EXIT_SUCCESS); break; case EOF: cont = 0; break; default: fprintf(stderr, "unknown option: %c\n", optchar); print_usage(); exit(EXIT_FAILURE); break; } } return 0; } static int set_scheduler(void) { struct sched_param sched_param; int err; err = sched_get_priority_max(SCHED_RR); if (err < 0) { log_error("Could not get maximum scheduler priority"); return err; } sched_param.sched_priority = err; err = sched_setscheduler(0, SCHED_RR, &sched_param); if (err < 0) log_error("could not set SCHED_RR priority %d", sched_param.sched_priority); return err; } static void remove_lockfile(void) { unlink(LOCKFILE_NAME); } static int create_lockfile(const char *lockfile) { int fd, value; size_t bufferlen; ssize_t write_out; struct flock lock; char buffer[50]; if ((fd = open(lockfile, O_CREAT | O_WRONLY, (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH))) < 0) { fprintf(stderr, "Cannot open lockfile [%s], error was [%s]\n", lockfile, strerror(errno)); return -1; } lock.l_type = F_WRLCK; lock.l_start = 0; lock.l_whence = SEEK_SET; lock.l_len = 0; retry_fcntl: if (fcntl(fd, F_SETLK, &lock) < 0) { switch (errno) { case EINTR: goto retry_fcntl; break; case EACCES: case EAGAIN: fprintf(stderr, "Cannot lock lockfile [%s], error was [%s]\n", lockfile, strerror(errno)); break; default: fprintf(stderr, "process is already running\n"); } goto fail_close; } if (ftruncate(fd, 0) < 0) { fprintf(stderr, "Cannot truncate pidfile [%s], error was [%s]\n", lockfile, strerror(errno)); goto fail_close_unlink; } memset(buffer, 0, sizeof(buffer)); snprintf(buffer, sizeof(buffer)-1, "%u\n", getpid()); bufferlen = strlen(buffer); write_out = write(fd, buffer, bufferlen); if ((write_out < 0) || (write_out == 0 && errno)) { fprintf(stderr, "Cannot write pid to pidfile [%s], error was [%s]\n", lockfile, strerror(errno)); goto fail_close_unlink; } if ((write_out == 0) || (write_out < bufferlen)) { fprintf(stderr, "Cannot write pid to pidfile [%s], shortwrite of" "[%zu] bytes, expected [%zu]\n", lockfile, write_out, bufferlen); goto fail_close_unlink; } if ((value = fcntl(fd, F_GETFD, 0)) < 0) { fprintf(stderr, "Cannot get close-on-exec flag from pidfile [%s], " "error was [%s]\n", lockfile, strerror(errno)); goto fail_close_unlink; } value |= FD_CLOEXEC; if (fcntl(fd, F_SETFD, value) < 0) { fprintf(stderr, "Cannot set close-on-exec flag from pidfile [%s], " "error was [%s]\n", lockfile, strerror(errno)); goto fail_close_unlink; } atexit(remove_lockfile); return 0; fail_close_unlink: if (unlink(lockfile)) fprintf(stderr, "Unable to unlink %s\n", lockfile); fail_close: if (close(fd)) fprintf(stderr, "Unable to close %s file descriptor\n", lockfile); return -1; } static void set_cfg_defaults(void) { if (!knet_cfg_head.conffile) knet_cfg_head.conffile = strdup(DEFAULT_CONFIG_FILE); if (!knet_cfg_head.conffile) { fprintf(stderr, "Unable to allocate memory for config file\n"); exit(EXIT_FAILURE); } if (!knet_cfg_head.logfile) knet_cfg_head.logfile = strdup(DEFAULT_LOG_FILE); if (!knet_cfg_head.conffile) { fprintf(stderr, "Unable to allocate memory for log file\n"); exit(EXIT_FAILURE); } if (!knet_cfg_head.vty_ip) knet_cfg_head.vty_ip = strdup("::"); if (!knet_cfg_head.vty_ip) { fprintf(stderr, "Unable to allocate memory for default ip address\n"); exit(EXIT_FAILURE); } if (!knet_cfg_head.vty_port) { char portbuf[8]; memset(&portbuf, 0, sizeof(portbuf)); snprintf(portbuf, sizeof(portbuf), "%d", KNET_VTY_DEFAULT_PORT); knet_cfg_head.vty_port = strdup(portbuf); } if (!knet_cfg_head.vty_port) { fprintf(stderr, "Unable to allocate memory for default port address\n"); exit(EXIT_FAILURE); } } int main(int argc, char **argv) { int err; memset(&knet_cfg_head, 0, sizeof(struct knet_cfg_top)); utils_syslog=1; if (create_lockfile(LOCKFILE_NAME) < 0) { fprintf(stderr, "Unable to create lockfile\n"); exit(EXIT_FAILURE); } if (read_arguments(argc, argv) < 0) { fprintf(stderr, "Unable to parse options\n"); exit(EXIT_FAILURE); } set_cfg_defaults(); if (daemonize) { if (daemon(0, 0) < 0) { perror("Unable to daemonize"); exit(EXIT_FAILURE); } } log_info(PACKAGE " version " VERSION); err = set_scheduler(); if (err < 0) goto out; if (knet_vty_main_loop() < 0) log_error("Detected fatal error in main loop"); out: if (knet_cfg_head.conffile) free(knet_cfg_head.conffile); if (knet_cfg_head.vty_ip) free(knet_cfg_head.vty_ip); if (knet_cfg_head.vty_port) free(knet_cfg_head.vty_port); return 0; } diff --git a/libvty/netutils.c b/libvty/netutils.c index 736793d9..630f24ae 100644 --- a/libvty/netutils.c +++ b/libvty/netutils.c @@ -1,112 +1,113 @@ #include "config.h" #include #include #include #include +#include +#include #include "netutils.h" -#include "utils.h" #define ADDRTOSTR_HOST_LEN 256 #define ADDRTOSTR_PORT_LEN 24 static int is_v4_mapped(struct sockaddr_storage *ss, socklen_t salen) { char map[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff }; struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *) ss; return memcmp(&addr6->sin6_addr, map, 12); } int cmpaddr(struct sockaddr_storage *ss1, socklen_t sslen1, struct sockaddr_storage *ss2, socklen_t sslen2) { int ss1_offset = 0, ss2_offset = 0; struct sockaddr_in6 *ss1_addr6 = (struct sockaddr_in6 *)ss1; struct sockaddr_in6 *ss2_addr6 = (struct sockaddr_in6 *)ss2; struct sockaddr_in *ss1_addr = (struct sockaddr_in *)ss1; struct sockaddr_in *ss2_addr = (struct sockaddr_in *)ss2; char *addr1, *addr2; if (ss1->ss_family == ss2->ss_family) return memcmp(ss1, ss2, sslen1); if (ss1->ss_family == AF_INET6) { if (is_v4_mapped(ss1, sslen1)) return 1; addr1 = (char *)&ss1_addr6->sin6_addr; ss1_offset = 12; } else addr1 = (char *)&ss1_addr->sin_addr; if (ss2->ss_family == AF_INET6) { if (is_v4_mapped(ss2, sslen2)) return 1; addr2 = (char *)&ss2_addr6->sin6_addr; ss2_offset = 12; } else addr2 = (char *)&ss2_addr->sin_addr; return memcmp(addr1+ss1_offset, addr2+ss2_offset, 4); } int strtoaddr(const char *host, const char *port, struct sockaddr *sa, socklen_t salen) { int ret; struct addrinfo hints; struct addrinfo *result = NULL; memset(&hints, 0, sizeof(struct addrinfo)); hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_DGRAM; hints.ai_flags = AI_NUMERICHOST | AI_NUMERICSERV; ret = getaddrinfo(host, port, &hints, &result); if (ret != 0) { errno = EINVAL; return -1; } memmove(sa, result->ai_addr, (salen < result->ai_addrlen) ? salen : result->ai_addrlen); freeaddrinfo(result); return ret; } int addrtostr(const struct sockaddr *sa, socklen_t salen, char *buf[2]) { int ret; buf[0] = malloc(ADDRTOSTR_HOST_LEN + ADDRTOSTR_PORT_LEN); if (buf[0] == NULL) return -1; buf[1] = buf[0] + ADDRTOSTR_HOST_LEN; ret = getnameinfo(sa, salen, buf[0], ADDRTOSTR_HOST_LEN, buf[1], ADDRTOSTR_PORT_LEN, NI_NUMERICHOST | NI_NUMERICSERV); if (ret != 0) { buf[0] = '\0'; buf[1] = '\0'; } else { buf[0][ADDRTOSTR_HOST_LEN - 1] = '\0'; buf[1][ADDRTOSTR_PORT_LEN - 1] = '\0'; } return ret; } void addrtostr_free(char *str[2]) { if (str[0] != NULL) free(str[0]); } diff --git a/libvty/vty.c b/libvty/vty.c index 72b8ab79..a5b4f777 100644 --- a/libvty/vty.c +++ b/libvty/vty.c @@ -1,340 +1,340 @@ #include "config.h" #include #include #include #include #include #include #include #include #include #include #include "cfg.h" -#include "utils.h" +#include "logging.h" #include "netutils.h" #include "vty.h" #include "vty_auth.h" #include "vty_cli.h" #include "vty_cli_cmds.h" #include "vty_utils.h" static int vty_max_connections = KNET_VTY_DEFAULT_MAX_CONN; static int vty_current_connections = 0; static int daemon_quit = 0; pthread_mutex_t knet_vty_mutex = PTHREAD_MUTEX_INITIALIZER; int knet_vty_config = -1; struct knet_vty knet_vtys[KNET_VTY_TOTAL_MAX_CONN]; static int knet_vty_init_listener(const char *ip_addr, const char *port) { int sockfd = -1, sockopt = 1, sockflags = -1; int socktype = SOCK_STREAM; int err = 0; struct sockaddr_storage ss; memset(&ss, 0, sizeof(struct sockaddr_storage)); if (strtoaddr(ip_addr, port, (struct sockaddr *)&ss, sizeof(struct sockaddr_storage)) != 0) return -1; pthread_mutex_lock(&knet_vty_mutex); /* handle sigpipe if we decide to use KEEPALIVE */ sockfd = socket(ss.ss_family, socktype, 0); if (sockfd < 0) { err = sockfd; goto out_clean; } err = setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (void *)&sockopt, sizeof(sockopt)); if (err) goto out_clean; sockflags = fcntl(sockfd, F_GETFD, 0); if (sockflags < 0) { err = -1; goto out_clean; } sockflags |= FD_CLOEXEC; if (fcntl(sockfd, F_SETFD, sockflags) < 0) { err = -1; goto out_clean; } err = bind(sockfd, (struct sockaddr *)&ss, sizeof(struct sockaddr_storage)); if (err) goto out_clean; err = listen(sockfd, 0); if (err) goto out_clean; pthread_mutex_unlock(&knet_vty_mutex); return sockfd; out_clean: if (sockfd >= 0) close(sockfd); pthread_mutex_unlock(&knet_vty_mutex); return err; } static void knet_vty_close_listener(int listener_fd) { pthread_mutex_lock(&knet_vty_mutex); if (listener_fd <= 0) goto out_clean; close(listener_fd); listener_fd = 0; out_clean: pthread_mutex_unlock(&knet_vty_mutex); return; } static void sigterm_handler(int sig) { daemon_quit = 1; } static void sigpipe_handler(int sig) { return; } static void knet_vty_close(struct knet_vty *vty) { if (knet_vty_config == vty->conn_num) knet_vty_config = -1; knet_vty_free_history(vty); vty->active = 0; close(vty->vty_sock); vty_current_connections--; } static void *vty_accept_thread(void *arg) { struct knet_vty *vty = (struct knet_vty *)&knet_vtys[*(int *)arg]; char *src_ip[2]; int err; knet_vty_print_banner(vty); if (vty->got_epipe) goto out_clean; src_ip[0] = NULL; err = addrtostr((struct sockaddr *)&vty->src_sa, vty->src_sa_len, src_ip); if (!err) { strncpy(vty->ip, src_ip[0], sizeof(vty->ip)); } else { strcpy(vty->ip, "unknown"); } if (src_ip[0]) addrtostr_free(src_ip); if ((knet_vty_auth_user(vty, NULL) < 0) && (!vty->got_epipe)) { log_info("User failed to authenticate (ip: %s)", vty->ip); goto out_clean; } if (vty->got_epipe) goto out_clean; log_info("User %s connected from %s", vty->username, vty->ip); knet_vty_write(vty, "Welcome %s (%s) on vty(%d)\n\n", vty->username, vty->ip, vty->conn_num); if (vty->got_epipe) goto out_clean; if (knet_vty_set_iacs(vty) < 0) { knet_vty_write(vty, "Unable to set telnet session preferences"); goto out_clean; } if (vty->got_epipe) goto out_clean; knet_vty_cli_bind(vty); out_clean: pthread_mutex_lock(&knet_vty_mutex); knet_vty_close(vty); pthread_mutex_unlock(&knet_vty_mutex); return NULL; } /* * mainloop is not thread safe as there should only be one */ int knet_vty_main_loop(void) { int vty_listener_fd; int vty_accept_fd; struct sockaddr_storage incoming_sa; socklen_t salen; fd_set rfds; int se_result = 0; struct timeval tv; int err = 0; int conn_index, found; signal(SIGTERM, sigterm_handler); signal(SIGINT, sigterm_handler); signal(SIGPIPE, sigpipe_handler); memset(&knet_vtys, 0, sizeof(knet_vtys)); if (knet_read_conf() < 0) { log_error("Unable to read config file %s", knet_cfg_head.conffile); return -1; } vty_listener_fd = knet_vty_init_listener(knet_cfg_head.vty_ip, knet_cfg_head.vty_port); if (vty_listener_fd < 0) { log_error("Unable to setup vty listener"); return -1; } while (se_result >= 0 && !daemon_quit) { FD_ZERO (&rfds); FD_SET (vty_listener_fd, &rfds); tv.tv_sec = 1; tv.tv_usec = 0; se_result = select((vty_listener_fd + 1), &rfds, 0, 0, &tv); if ((se_result == -1) && (daemon_quit)) { log_info("Got a SIGTERM, requesting CLI threads to exit"); for(conn_index = 0; conn_index < KNET_VTY_TOTAL_MAX_CONN; conn_index++) { if (knet_vtys[conn_index].active) { knet_vty_write(&knet_vtys[conn_index], "%s%sServer is going down..%s%s", telnet_newline, telnet_newline, telnet_newline, telnet_newline); knet_vty_close(&knet_vtys[conn_index]); knet_vtys[conn_index].got_epipe = 1; } } sleep(2); log_info("Have a nice day! Goodbye"); goto out; } if (se_result == -1) { err = se_result; log_error("Unable to select on vty listener socket!"); goto out; } if (se_result == 0) { pthread_mutex_lock(&knet_vty_mutex); for(conn_index = 0; conn_index < KNET_VTY_TOTAL_MAX_CONN; conn_index++) { if ((knet_vtys[conn_index].active) && (!knet_vtys[conn_index].disable_idle)) { knet_vtys[conn_index].idle++; if (knet_vtys[conn_index].idle > KNET_VTY_CLI_TIMEOUT) { knet_vty_close(&knet_vtys[conn_index]); knet_vtys[conn_index].got_epipe = 1; } } } pthread_mutex_unlock(&knet_vty_mutex); continue; } if (!FD_ISSET(vty_listener_fd, &rfds)) continue; memset(&incoming_sa, 0, sizeof(struct sockaddr_storage)); salen = sizeof(struct sockaddr_storage); vty_accept_fd = accept(vty_listener_fd, (struct sockaddr *)&incoming_sa, &salen); if (vty_accept_fd < 0) { log_error("Unable to accept connection to vty"); continue; } // check for ip address access list here against incoming_sa pthread_mutex_lock(&knet_vty_mutex); found = 0; for(conn_index = 0; conn_index <= vty_max_connections; conn_index++) { if (knet_vtys[conn_index].active == 0) { found = 1; break; } } if ((vty_current_connections == vty_max_connections) || (!found)) { errno = ECONNREFUSED; log_error("Too many connections to VTY or no available slots"); close(vty_accept_fd); pthread_mutex_unlock(&knet_vty_mutex); continue; } vty_current_connections++; memset(&knet_vtys[conn_index], 0, sizeof(struct knet_vty)); knet_vtys[conn_index].vty_sock = vty_accept_fd; knet_vtys[conn_index].conn_num = conn_index; memcpy(&knet_vtys[conn_index].src_sa, &incoming_sa, salen); knet_vtys[conn_index].src_sa_len = salen; knet_vtys[conn_index].active = 1; err = pthread_create(&knet_vtys[conn_index].vty_thread, NULL, vty_accept_thread, (void *)&conn_index); if (err < 0) { log_error("Unable to spawn vty thread"); memset(&knet_vtys[conn_index], 0, sizeof(struct knet_vty)); vty_current_connections--; } pthread_mutex_unlock(&knet_vty_mutex); } out: knet_vty_close_listener(vty_listener_fd); // reverse running config to close/release resources; return err; } /* int knet_vty_set_max_connections(const int max_connections) { int err = 0; pthread_mutex_lock(&knet_vty_mutex); if ((max_connections > KNET_VTY_TOTAL_MAX_CONN) || (max_connections < 1)) { errno = EINVAL; err = -1; } else { vty_max_connections = max_connections; } pthread_mutex_unlock(&knet_vty_mutex); return err; } */ diff --git a/libvty/vty_auth.c b/libvty/vty_auth.c index adfbeb62..dd46a3df 100644 --- a/libvty/vty_auth.c +++ b/libvty/vty_auth.c @@ -1,290 +1,290 @@ #include "config.h" #include #include #include #include #include #include #include #include #include -#include "utils.h" +#include "logging.h" #include "vty_auth.h" #include "vty_utils.h" static int knet_pam_misc_conv(int num_msg, const struct pam_message **msgm, struct pam_response **response, void *appdata_ptr) { int count = 0; struct pam_response *reply; struct knet_vty *vty = (struct knet_vty *)appdata_ptr; if (num_msg <= 0) return PAM_CONV_ERR; reply = (struct pam_response *) calloc(num_msg, sizeof(struct pam_response)); if (reply == NULL) return PAM_CONV_ERR; for (count=0; count < num_msg; ++count) { unsigned char readbuf[VTY_MAX_BUFFER_SIZE]; char *string=NULL; int nc; memset(readbuf, 0, sizeof(readbuf)); switch (msgm[count]->msg_style) { case PAM_PROMPT_ECHO_OFF: if (knet_vty_set_echo(vty, 0) < 0) { knet_vty_write(vty, "Unable to turn off terminal/telnet echo"); goto failed_conversation; } knet_vty_write(vty, "%s", msgm[count]->msg); nc = knet_vty_read(vty, readbuf, sizeof(readbuf)); if (nc < 0) goto failed_conversation; if (knet_vty_set_echo(vty, 1) < 0) { /* doesn't really make a lot of sense tho.... */ knet_vty_write(vty, "Unable to turn on terminal/telnet echo"); goto failed_conversation; } knet_vty_write(vty, "\n"); readbuf[nc-2] = 0; string = strdup((const char*)readbuf); if (!string) goto failed_conversation; break; case PAM_PROMPT_ECHO_ON: knet_vty_write(vty, "\n%s", msgm[count]->msg); nc = knet_vty_read(vty, readbuf, sizeof(readbuf)); if (nc < 0) goto failed_conversation; readbuf[nc-2] = 0; string = strdup((const char*)readbuf); if (!string) goto failed_conversation; break; case PAM_ERROR_MSG: log_error("Received PAM error message %s", msgm[count]->msg); knet_vty_write(vty, "%s", msgm[count]->msg); break; case PAM_TEXT_INFO: log_error("Received PAM text info: %s", msgm[count]->msg); knet_vty_write(vty, "%s", msgm[count]->msg); break; default: if (!vty->got_epipe) { log_error("Unknown PAM conversation message"); knet_vty_write(vty, "Unknown PAM conversation message"); } goto failed_conversation; } if (string) { reply[count].resp_retcode = 0; reply[count].resp = string; string = NULL; } } *response = reply; reply = NULL; return PAM_SUCCESS; failed_conversation: if (!vty->got_epipe) { log_error("PAM conversation error"); knet_vty_write(vty, "PAM conversation error"); } if (reply) { for (count=0; count < num_msg; ++count) { if (reply[count].resp == NULL) continue; switch (msgm[count]->msg_style) { case PAM_PROMPT_ECHO_ON: case PAM_PROMPT_ECHO_OFF: _pam_overwrite(reply[count].resp); free(reply[count].resp); break; case PAM_BINARY_PROMPT: { void *bt_ptr = reply[count].resp; pam_binary_handler_free(appdata_ptr, bt_ptr); break; } case PAM_ERROR_MSG: case PAM_TEXT_INFO: free(reply[count].resp); } } free(reply); reply = NULL; } return PAM_CONV_ERR; } static int knet_vty_get_pam_user(struct knet_vty *vty, pam_handle_t *pamh) { const void *value; int err; memset(vty->username, 0, sizeof(vty->username)); err = pam_get_item(pamh, PAM_USER, &value); if (err != PAM_SUCCESS) return err; strncpy(vty->username, (const char*)value, 32); return 0; } static int knet_vty_pam_auth_user(struct knet_vty *vty, const char *user) { pam_handle_t *pamh=NULL; struct pam_conv conv; int err; int retry = 1; conv.conv = knet_pam_misc_conv; conv.appdata_ptr = (void *)vty; retry_auth: err = pam_start("kronosnet", user, &conv, &pamh); if (err != PAM_SUCCESS) { errno = EINVAL; log_error("PAM fatal error: %s", pam_strerror(pamh, err)); knet_vty_write(vty, "PAM fatal error: %s", pam_strerror(pamh, err)); goto out_fatal; } err = pam_authenticate(pamh, 0); if (err != PAM_SUCCESS) { if (vty->got_epipe) { errno = EPIPE; goto out_fatal; } else { errno = EINVAL; goto out_clean; } } if (knet_vty_get_pam_user(vty, pamh) != PAM_SUCCESS) { log_error("PAM: unable to get PAM_USER: %s", pam_strerror(pamh, err)); knet_vty_write(vty, "PAM: unable to get PAM_USER: %s", pam_strerror(pamh, err)); goto out_clean; } err = pam_acct_mgmt(pamh, 0); if (err != PAM_SUCCESS) { log_info("User: %s failed to authenticate on vty(%d) attempt %d", vty->username, vty->conn_num, retry); goto out_clean; } out_clean: if (pamh) { pam_end(pamh, err); pamh = NULL; } if ((err != PAM_SUCCESS) && (retry < AUTH_MAX_RETRY)) { retry++; goto retry_auth; } out_fatal: if (pamh) { pam_end(pamh, err); pamh = NULL; } knet_vty_write(vty, "\n"); return err; } static int knet_vty_group_check(struct knet_vty *vty) { struct group grp; char *buf; size_t buflen; long int initlen; struct group *result; char *gr_mem; int err, i; errno = 0; initlen = sysconf(_SC_GETGR_R_SIZE_MAX); if ((initlen < 0) && (errno == EINVAL)) return -1; if (initlen < 0) initlen = 1024; buflen = (size_t) initlen; buf = malloc(buflen); if (!buf) return -1; while ((err = getgrnam_r(DEFAULTADMGROUP, &grp, buf, buflen, &result)) == ERANGE) { size_t newlen = 2 * buflen; char *newbuf; newbuf = realloc(buf, newlen); if (!newbuf) { err = -1; goto out_clean; } buf = newbuf; } if (err) goto out_clean; if (result == NULL) { errno = EACCES; log_error("No " DEFAULTADMGROUP " group found on the system"); knet_vty_write(vty, "No " DEFAULTADMGROUP " group found on the system\n"); err = -1; goto out_clean; } gr_mem = *grp.gr_mem; i = 0; while(gr_mem != NULL) { if (!strcmp(vty->username, gr_mem)) { vty->user_can_enable = 1; break; } gr_mem = *(grp.gr_mem + i); i++; } out_clean: free(buf); return err; } int knet_vty_auth_user(struct knet_vty *vty, const char *user) { int err; err = knet_vty_pam_auth_user(vty, user); if (err != PAM_SUCCESS) return -1; return knet_vty_group_check(vty); } diff --git a/libvty/vty_cli.c b/libvty/vty_cli.c index df78f120..b9b4d6a5 100644 --- a/libvty/vty_cli.c +++ b/libvty/vty_cli.c @@ -1,522 +1,522 @@ #include "config.h" #include #include #include #include -#include "utils.h" +#include "logging.h" #include "vty.h" #include "vty_cli.h" #include "vty_cli_cmds.h" #include "vty_utils.h" /* if this code looks like quagga lib/vty.c it is because we stole it in good part */ #define CONTROL(X) ((X) - '@') #define VTY_NORMAL 0 #define VTY_PRE_ESCAPE 1 #define VTY_ESCAPE 2 #define VTY_EXT_ESCAPE 3 static void knet_vty_reset_buf(struct knet_vty *vty) { memset(vty->line, 0, sizeof(vty->line)); vty->line_idx = 0; vty->cursor_pos = 0; vty->history_pos = vty->history_idx; } static void knet_vty_add_to_buf(struct knet_vty *vty, unsigned char *buf, int pos) { char outbuf[2]; int i; if (vty->cursor_pos == vty->line_idx) { vty->line[vty->line_idx] = buf[pos]; vty->line_idx++; vty->cursor_pos++; } else { if (!vty->insert_mode) { memmove(&vty->line[vty->cursor_pos+1], &vty->line[vty->cursor_pos], vty->line_idx - vty->cursor_pos); vty->line_idx++; } vty->line[vty->cursor_pos] = buf[pos]; vty->cursor_pos++; } outbuf[0] = buf[pos]; outbuf[1] = 0; knet_vty_write(vty, "%s%s", outbuf, &vty->line[vty->cursor_pos]); for (i = 0; i < (vty->line_idx - vty->cursor_pos); i++) knet_vty_write(vty, "%s", telnet_backward_char); } static void knet_vty_forward_char(struct knet_vty *vty) { char buf[2]; if (vty->cursor_pos < vty->line_idx) { buf[0] = vty->line[vty->cursor_pos]; buf[1] = 0; knet_vty_write(vty, "%s", buf); vty->cursor_pos++; } } static void knet_vty_backward_char(struct knet_vty *vty) { if (vty->cursor_pos > 0) { knet_vty_write(vty, "%s", telnet_backward_char); vty->cursor_pos--; } } static void knet_vty_kill_line(struct knet_vty *vty) { int size, i; size = vty->line_idx - vty->cursor_pos; if (size == 0) return; for (i = 0; i < size; i++) knet_vty_write(vty, " "); for (i = 0; i < size; i++) knet_vty_write(vty, "%s", telnet_backward_char); memset(&vty->line[vty->cursor_pos], 0, size); vty->line_idx = vty->cursor_pos; } static void knet_vty_newline(struct knet_vty *vty) { knet_vty_write(vty, "%s", telnet_newline); knet_vty_reset_buf(vty); knet_vty_prompt(vty); } static void knet_vty_delete_char(struct knet_vty *vty) { int size, i; if (vty->line_idx == 0) { knet_vty_exit_node(vty); if (!vty->got_epipe) { knet_vty_newline(vty); } return; } if (vty->line_idx == vty->cursor_pos) return; size = vty->line_idx - vty->cursor_pos; vty->line_idx--; memmove(&vty->line[vty->cursor_pos], &vty->line[vty->cursor_pos+1], size - 1); vty->line[vty->line_idx] = '\0'; knet_vty_write(vty, "%s ", &vty->line[vty->cursor_pos]); for (i = 0; i < size; i++) knet_vty_write(vty, "%s", telnet_backward_char); } static void knet_vty_delete_backward_char(struct knet_vty *vty) { if (vty->cursor_pos == 0) return; knet_vty_backward_char(vty); knet_vty_delete_char(vty); } static void knet_vty_beginning_of_line(struct knet_vty *vty) { while (vty->cursor_pos != 0) knet_vty_backward_char(vty); } static void knet_vty_end_of_line(struct knet_vty *vty) { while (vty->cursor_pos != vty->line_idx) knet_vty_forward_char(vty); } static void knet_vty_kill_line_from_beginning(struct knet_vty *vty) { knet_vty_beginning_of_line(vty); knet_vty_kill_line(vty); } static void knet_vty_backward_word(struct knet_vty *vty) { while(vty->cursor_pos > 0 && vty->line[vty->cursor_pos - 1] == ' ') knet_vty_backward_char(vty); while(vty->cursor_pos > 0 && vty->line[vty->cursor_pos - 1] != ' ') knet_vty_backward_char(vty); } static void knet_vty_forward_word(struct knet_vty *vty) { while(vty->cursor_pos != vty->line_idx && vty->line[vty->cursor_pos] == ' ') knet_vty_forward_char(vty); while(vty->cursor_pos != vty->line_idx && vty->line[vty->cursor_pos] != ' ') knet_vty_forward_char(vty); } static void knet_vty_backward_kill_word(struct knet_vty *vty) { while(vty->cursor_pos > 0 && vty->line[vty->cursor_pos - 1] == ' ') knet_vty_delete_backward_char(vty); while(vty->cursor_pos > 0 && vty->line[vty->cursor_pos - 1] != ' ') knet_vty_delete_backward_char(vty); } static void knet_vty_forward_kill_word(struct knet_vty *vty) { while(vty->cursor_pos != vty->line_idx && vty->line[vty->cursor_pos] == ' ') knet_vty_delete_char(vty); while(vty->cursor_pos != vty->line_idx && vty->line[vty->cursor_pos] != ' ') knet_vty_delete_backward_char(vty); } static void knet_vty_transpose_chars(struct knet_vty *vty) { unsigned char swap[2]; if (vty->line_idx < 2 || vty->cursor_pos < 2) return; swap[0] = vty->line[vty->cursor_pos - 1]; swap[1] = vty->line[vty->cursor_pos - 2]; knet_vty_delete_backward_char(vty); knet_vty_delete_backward_char(vty); knet_vty_add_to_buf(vty, swap, 0); knet_vty_add_to_buf(vty, swap, 1); } static void knet_vty_history_add(struct knet_vty *vty) { int idx; if (knet_vty_is_line_empty(vty)) return; idx = vty->history_idx % KNET_VTY_MAX_HIST; if (vty->history[idx]) { free(vty->history[idx]); vty->history[idx] = NULL; } vty->history[idx] = strdup(vty->line); if (vty->history[idx] == NULL) { log_error("Not enough memory to add history lines!"); knet_vty_write(vty, "Not enough memory to add history lines!"); } vty->history_idx++; if (vty->history_idx == KNET_VTY_MAX_HIST) vty->history_idx = 0; vty->history_pos = vty->history_idx; } static void knet_vty_history_print(struct knet_vty *vty) { int len; knet_vty_kill_line_from_beginning(vty); len = strlen(vty->history[vty->history_pos]); memcpy(vty->line, vty->history[vty->history_pos], len); vty->cursor_pos = vty->line_idx = len; knet_vty_write(vty, "%s", vty->line); } static void knet_vty_history_prev(struct knet_vty *vty) { int idx; idx = vty->history_pos; if (idx == 0) { idx = KNET_VTY_MAX_HIST - 1; } else { idx--; } if (vty->history[idx] == NULL) return; vty->history_pos = idx; knet_vty_history_print(vty); } static void knet_vty_history_next(struct knet_vty *vty) { int idx; if (vty->history_pos == vty->history_idx) return; idx = vty->history_pos; if (idx == (KNET_VTY_MAX_HIST - 1)) { idx = 0; } else { idx++; } if (vty->history[idx] == NULL) { knet_vty_kill_line_from_beginning(vty); vty->history_pos = vty->history_idx; return; } vty->history_pos = idx; knet_vty_history_print(vty); } static int knet_vty_process_buf(struct knet_vty *vty, unsigned char *buf, int buflen) { int i; if (vty->line_idx >= KNET_VTY_MAX_LINE) return -1; for (i = 0; i < buflen; i++) { if (vty->escape == VTY_EXT_ESCAPE) { if (buf[i] != '~') goto vty_ext_escape_out; switch (vty->escape_code) { case ('1'): knet_vty_beginning_of_line(vty); break; case ('2'): if (!vty->insert_mode) { vty->insert_mode = 1; } else { vty->insert_mode = 0; } break; case ('3'): knet_vty_delete_char(vty); break; case ('4'): knet_vty_end_of_line(vty); break; case ('5'): knet_vty_history_prev(vty); break; case ('6'): knet_vty_history_next(vty); break; } vty_ext_escape_out: vty->escape = VTY_NORMAL; continue; } if (vty->escape == VTY_ESCAPE) { switch (buf[i]) { case ('A'): knet_vty_history_prev(vty); break; case ('B'): knet_vty_history_next(vty); break; case ('C'): knet_vty_forward_char(vty); break; case ('D'): knet_vty_backward_char(vty); break; case ('H'): knet_vty_beginning_of_line(vty); break; case ('F'): knet_vty_end_of_line(vty); break; case ('1'): case ('2'): case ('3'): case ('4'): case ('5'): case ('6'): vty->escape = VTY_EXT_ESCAPE; vty->escape_code = buf[i]; break; default: break; } if (vty->escape == VTY_ESCAPE) vty->escape = VTY_NORMAL; continue; } if (vty->escape == VTY_PRE_ESCAPE) { switch (buf[i]) { case 'O': case '[': vty->escape = VTY_ESCAPE; break; case 'b': vty->escape = VTY_NORMAL; knet_vty_backward_word(vty); break; case 'f': vty->escape = VTY_NORMAL; knet_vty_forward_word(vty); break; case 'd': vty->escape = VTY_NORMAL; knet_vty_forward_kill_word(vty); break; case CONTROL('H'): case 0x7f: vty->escape = VTY_NORMAL; knet_vty_backward_kill_word(vty); break; default: break; } continue; } switch (buf[i]) { case CONTROL('A'): knet_vty_beginning_of_line(vty); break; case CONTROL('B'): knet_vty_backward_char(vty); break; case CONTROL('C'): knet_vty_newline(vty); break; case CONTROL('D'): knet_vty_delete_char(vty); break; case CONTROL('E'): knet_vty_end_of_line(vty); break; case CONTROL('F'): knet_vty_forward_char(vty); break; case CONTROL('H'): case 0x7f: knet_vty_delete_backward_char(vty); break; case CONTROL('K'): knet_vty_kill_line(vty); break; case CONTROL('N'): knet_vty_history_next(vty); break; case CONTROL('P'): knet_vty_history_prev(vty); break; case CONTROL('T'): knet_vty_transpose_chars(vty); break; case CONTROL('U'): knet_vty_kill_line_from_beginning(vty); break; case CONTROL('W'): knet_vty_backward_kill_word(vty); break; case CONTROL('Z'): vty->node = NODE_CONFIG; knet_vty_exit_node(vty); knet_vty_newline(vty); break; case '\n': case '\r': knet_vty_end_of_line(vty); knet_vty_write(vty, "%s", telnet_newline); knet_vty_history_add(vty); knet_vty_execute_cmd(vty); knet_vty_reset_buf(vty); knet_vty_prompt(vty); break; case '\t': knet_vty_end_of_line(vty); knet_vty_tab_completion(vty); break; case '?': knet_vty_end_of_line(vty); knet_vty_write(vty, "%s", telnet_newline); knet_vty_help(vty); knet_vty_prompt(vty); knet_vty_write(vty, "%s", vty->line); break; case '\033': vty->escape = VTY_PRE_ESCAPE; break; default: if (buf[i] > 31 && buf[i] < 127) knet_vty_add_to_buf(vty, buf, i); break; } } return 0; } void knet_vty_cli_bind(struct knet_vty *vty) { int se_result = 0; fd_set rfds; struct timeval tv; unsigned char buf[VTY_MAX_BUFFER_SIZE]; int readlen; knet_vty_prompt(vty); while (se_result >= 0 && !vty->got_epipe) { FD_ZERO (&rfds); FD_SET (vty->vty_sock, &rfds); tv.tv_sec = 1; tv.tv_usec = 0; se_result = select((vty->vty_sock + 1), &rfds, 0, 0, &tv); if ((se_result == -1) || (vty->got_epipe)) goto out_clean; if ((se_result == 0) || (!FD_ISSET(vty->vty_sock, &rfds))) continue; memset(buf, 0 , sizeof(buf)); readlen = knet_vty_read(vty, buf, sizeof(buf)); if (readlen <= 0) goto out_clean; if (knet_vty_process_buf(vty, buf, readlen) < 0) { knet_vty_write(vty, "\nError processing command: command too long\n"); knet_vty_reset_buf(vty); } } out_clean: return; } diff --git a/libvty/vty_cli_cmds.c b/libvty/vty_cli_cmds.c index ffa6eaa9..fc618486 100644 --- a/libvty/vty_cli_cmds.c +++ b/libvty/vty_cli_cmds.c @@ -1,1549 +1,1549 @@ #include "config.h" #include #include #include #include #include #include #include #include "cfg.h" -#include "utils.h" +#include "logging.h" #include "libtap.h" #include "netutils.h" #include "vty.h" #include "vty_cli.h" #include "vty_cli_cmds.h" #include "vty_utils.h" #define KNET_VTY_MAX_MATCHES 64 #define KNET_VTY_MATCH_HELP 0 #define KNET_VTY_MATCH_EXEC 1 #define KNET_VTY_MATCH_EXPAND 2 #define CMDS_PARAM_NOMORE 0 #define CMDS_PARAM_KNET 1 #define CMDS_PARAM_IP 2 #define CMDS_PARAM_IP_PREFIX 3 #define CMDS_PARAM_IP_PORT 4 #define CMDS_PARAM_BOOL 5 #define CMDS_PARAM_INT 6 #define CMDS_PARAM_NODEID 7 #define CMDS_PARAM_NAME 8 #define CMDS_PARAM_MTU 9 /* * CLI helper functions - menu/node stuff starts below */ /* * return 0 if we find a command in vty->line and cmd/len/no are set * return -1 if we cannot find a command. no can be trusted. cmd/len would be empty */ static int get_command(struct knet_vty *vty, char **cmd, int *cmdlen, int *cmdoffset, int *no) { int start = 0, idx; for (idx = 0; idx < vty->line_idx; idx++) { if (vty->line[idx] != ' ') break; } if (!strncmp(&vty->line[idx], "no ", 3)) { *no = 1; idx = idx + 3; for (idx = idx; idx < vty->line_idx; idx++) { if (vty->line[idx] != ' ') break; } } else { *no = 0; } start = idx; if (start == vty->line_idx) return -1; *cmd = &vty->line[start]; *cmdoffset = start; for (idx = start; idx < vty->line_idx; idx++) { if (vty->line[idx] == ' ') break; } *cmdlen = idx - start; return 0; } /* * still not sure why I need to count backwards... */ static void get_n_word_from_end(struct knet_vty *vty, int n, char **word, int *wlen, int *woffset) { int widx; int idx, end, start; start = end = vty->line_idx; for (widx = 0; widx < n; widx++) { for (idx = start - 1; idx > 0; idx--) { if (vty->line[idx] != ' ') break; } end = idx; for (idx = end; idx > 0; idx--) { if (vty->line[idx-1] == ' ') break; } start = idx; } *wlen = (end - start) + 1; *word = &vty->line[start]; *woffset = start; } static int expected_params(const vty_param_t *params) { int idx = 0; while(params[idx].param != CMDS_PARAM_NOMORE) idx++; return idx; } static int count_words(struct knet_vty *vty, int offset) { int idx, widx = 0; int status = 0; for (idx = offset; idx < vty->line_idx; idx++) { if (vty->line[idx] == ' ') { status = 0; continue; } if ((vty->line[idx] != ' ') && (!status)) { widx++; status = 1; continue; } } return widx; } static int param_to_int(const char *param, int paramlen) { char buf[KNET_VTY_MAX_LINE]; memset(buf, 0, sizeof(buf)); memcpy(buf, param, paramlen); return atoi(buf); } static int param_to_str(char *buf, int bufsize, const char *param, int paramlen) { if (bufsize < paramlen) return -1; memset(buf, 0, bufsize); memcpy(buf, param, paramlen); return paramlen; } static const vty_node_cmds_t *get_cmds(struct knet_vty *vty, char **cmd, int *cmdlen, int *cmdoffset) { int no; const vty_node_cmds_t *cmds = knet_vty_nodes[vty->node].cmds; get_command(vty, cmd, cmdlen, cmdoffset, &no); if (no) cmds = knet_vty_nodes[vty->node].no_cmds; return cmds; } static int check_param(struct knet_vty *vty, const int paramtype, char *param, int paramlen) { int err = 0; char buf[KNET_VTY_MAX_LINE]; int tmp; memset(buf, 0, sizeof(buf)); switch(paramtype) { case CMDS_PARAM_NOMORE: break; case CMDS_PARAM_KNET: if (paramlen >= IFNAMSIZ) { knet_vty_write(vty, "interface name too long%s", telnet_newline); err = -1; } break; case CMDS_PARAM_IP: break; case CMDS_PARAM_IP_PREFIX: break; case CMDS_PARAM_IP_PORT: tmp = param_to_int(param, paramlen); if ((tmp < 0) || (tmp > 65279)) { knet_vty_write(vty, "port number must be a value between 0 and 65279%s", telnet_newline); err = -1; } break; case CMDS_PARAM_BOOL: break; case CMDS_PARAM_INT: break; case CMDS_PARAM_NODEID: tmp = param_to_int(param, paramlen); if ((tmp < 0) || (tmp > 255)) { knet_vty_write(vty, "node id must be a value between 0 and 255%s", telnet_newline); err = -1; } break; case CMDS_PARAM_NAME: if (paramlen >= KNET_MAX_HOST_LEN) { knet_vty_write(vty, "name cannot exceed %d char in len%s", KNET_MAX_HOST_LEN - 1, telnet_newline); } break; case CMDS_PARAM_MTU: tmp = param_to_int(param, paramlen); if ((tmp < 576) || (tmp > 65536)) { knet_vty_write(vty, "mtu should be a value between 576 and 65536 (note: max value depends on the media)%s", telnet_newline); err = -1; } break; default: knet_vty_write(vty, "CLI ERROR: unknown parameter type%s", telnet_newline); err = -1; break; } return err; } static void describe_param(struct knet_vty *vty, const int paramtype) { switch(paramtype) { case CMDS_PARAM_NOMORE: knet_vty_write(vty, "no more parameters%s", telnet_newline); break; case CMDS_PARAM_KNET: knet_vty_write(vty, "KNET_IFACE_NAME - interface name (max %d chars) eg: kronosnet0%s", IFNAMSIZ, telnet_newline); break; case CMDS_PARAM_IP: knet_vty_write(vty, "IP address - ipv4 or ipv6 address to add/remove%s", telnet_newline); break; case CMDS_PARAM_IP_PREFIX: knet_vty_write(vty, "IP prefix len (eg. 24, 64)%s", telnet_newline); break; case CMDS_PARAM_IP_PORT: knet_vty_write(vty, "base port (eg: %d) %s", KNET_RING_DEFPORT, telnet_newline); case CMDS_PARAM_BOOL: break; case CMDS_PARAM_INT: break; case CMDS_PARAM_NODEID: knet_vty_write(vty, "NODEID - unique identifier for this interface in this kronos network (value between 0 and 255)%s", telnet_newline); break; case CMDS_PARAM_NAME: knet_vty_write(vty, "NAME - unique name identifier for this entity (max %d chars)%s", KNET_MAX_HOST_LEN - 1, telnet_newline); break; case CMDS_PARAM_MTU: knet_vty_write(vty, "MTU - a value between 576 and 65536 (note: max value depends on the media)%s", telnet_newline); break; default: /* this should never happen */ knet_vty_write(vty, "CLI ERROR: unknown parameter type%s", telnet_newline); break; } } static void print_help(struct knet_vty *vty, const vty_node_cmds_t *cmds, int idx) { if ((idx < 0) || (cmds == NULL) || (cmds[idx].cmd == NULL)) return; if (cmds[idx].help != NULL) { knet_vty_write(vty, "%s\t%s%s", cmds[idx].cmd, cmds[idx].help, telnet_newline); } else { knet_vty_write(vty, "%s\tNo help available for this command%s", cmds[idx].cmd, telnet_newline); } } static int get_param(struct knet_vty *vty, int wanted_paranum, char **param, int *paramlen, int *paramoffset) { int eparams, tparams; const vty_param_t *params = (const vty_param_t *)vty->param; int paramstart = vty->paramoffset; eparams = expected_params(params); tparams = count_words(vty, paramstart); if (tparams > eparams) return -1; if (wanted_paranum == -1) { get_n_word_from_end(vty, 1, param, paramlen, paramoffset); return tparams; } if (tparams < wanted_paranum) return -1; get_n_word_from_end(vty, (tparams - wanted_paranum) + 1, param, paramlen, paramoffset); return tparams - wanted_paranum; } static int match_command(struct knet_vty *vty, const vty_node_cmds_t *cmds, char *cmd, int cmdlen, int cmdoffset, int mode) { int idx = 0, found = -1, paramoffset = 0, paramlen = 0, last_param = 0; char *param = NULL; int paramstart = cmdlen + cmdoffset; int matches[KNET_VTY_MAX_MATCHES]; memset(&matches, -1, sizeof(matches)); while ((cmds[idx].cmd != NULL) && (idx < KNET_VTY_MAX_MATCHES)) { if (!strncmp(cmds[idx].cmd, cmd, cmdlen)) { found++; matches[found] = idx; } idx++; } if (idx >= KNET_VTY_MAX_MATCHES) { knet_vty_write(vty, "Too many matches for this command%s", telnet_newline); return -1; } if (found < 0) { knet_vty_write(vty, "There is no such command%s", telnet_newline); return -1; } switch(mode) { case KNET_VTY_MATCH_HELP: if (found == 0) { if ((cmdoffset <= vty->cursor_pos) && (vty->cursor_pos <= paramstart)) { print_help(vty, cmds, matches[0]); break; } if (cmds[matches[0]].params != NULL) { vty->param = (void *)cmds[matches[0]].params; vty->paramoffset = paramstart; last_param = get_param(vty, -1, ¶m, ¶mlen, ¶moffset); if ((paramoffset <= vty->cursor_pos) && (vty->cursor_pos <= (paramoffset + paramlen))) last_param--; if (last_param >= CMDS_PARAM_NOMORE) { describe_param(vty, cmds[matches[0]].params[last_param].param); if (paramoffset > 0) check_param(vty, cmds[matches[0]].params[last_param].param, param, paramlen); } break; } } if (found >= 0) { idx = 0; while (matches[idx] >= 0) { print_help(vty, cmds, matches[idx]); idx++; } } break; case KNET_VTY_MATCH_EXEC: if (found == 0) { int exec = 0; if (cmds[matches[0]].params != NULL) { int eparams, tparams; eparams = expected_params(cmds[matches[0]].params); tparams = count_words(vty, paramstart); if (eparams != tparams) { exec = -1; idx = 0; knet_vty_write(vty, "Parameter required for this command:%s", telnet_newline); while(cmds[matches[0]].params[idx].param != CMDS_PARAM_NOMORE) { describe_param(vty, cmds[matches[0]].params[idx].param); idx++; } break; } idx = 0; vty->param = (void *)cmds[matches[0]].params; vty->paramoffset = paramstart; while(cmds[matches[0]].params[idx].param != CMDS_PARAM_NOMORE) { get_param(vty, idx + 1, ¶m, ¶mlen, ¶moffset); if (check_param(vty, cmds[matches[0]].params[idx].param, param, paramlen) < 0) { exec = -1; if (vty->filemode) return -1; } idx++; } } if (!exec) { if (cmds[matches[0]].params != NULL) { vty->param = (void *)cmds[matches[0]].params; vty->paramoffset = paramstart; } if (cmds[matches[0]].func != NULL) { return cmds[matches[0]].func(vty); } else { /* this will eventually disappear */ knet_vty_write(vty, "no fn associated to this command%s", telnet_newline); } } } if (found > 0) { knet_vty_write(vty, "Ambiguous command.%s", telnet_newline); } break; case KNET_VTY_MATCH_EXPAND: if (found == 0) { int cmdreallen; if (vty->cursor_pos > cmdoffset+cmdlen) /* complete param? */ break; cmdreallen = strlen(cmds[matches[0]].cmd); memset(vty->line + cmdoffset, 0, cmdlen); memcpy(vty->line + cmdoffset, cmds[matches[0]].cmd, cmdreallen); vty->line[cmdreallen + cmdoffset] = ' '; vty->line_idx = cmdreallen + cmdoffset + 1; vty->cursor_pos = cmdreallen + cmdoffset + 1; } if (found > 0) { /* add completion to string base root */ int count = 0; idx = 0; while (matches[idx] >= 0) { knet_vty_write(vty, "%s\t\t", cmds[matches[idx]].cmd); idx++; count++; if (count == 4) { knet_vty_write(vty, "%s",telnet_newline); count = 0; } } knet_vty_write(vty, "%s",telnet_newline); } break; default: /* this should never really happen */ log_info("Unknown match mode"); break; } return found; } /* forward declarations */ /* common to almost all nodes */ static int knet_cmd_logout(struct knet_vty *vty); static int knet_cmd_who(struct knet_vty *vty); static int knet_cmd_exit_node(struct knet_vty *vty); static int knet_cmd_help(struct knet_vty *vty); /* root node */ static int knet_cmd_config(struct knet_vty *vty); /* config node */ static int knet_cmd_interface(struct knet_vty *vty); static int knet_cmd_no_interface(struct knet_vty *vty); static int knet_cmd_show_conf(struct knet_vty *vty); static int knet_cmd_write_conf(struct knet_vty *vty); /* interface node */ static int knet_cmd_mtu(struct knet_vty *vty); static int knet_cmd_no_mtu(struct knet_vty *vty); static int knet_cmd_ip(struct knet_vty *vty); static int knet_cmd_no_ip(struct knet_vty *vty); static int knet_cmd_baseport(struct knet_vty *vty); static int knet_cmd_peer(struct knet_vty *vty); static int knet_cmd_no_peer(struct knet_vty *vty); static int knet_cmd_start(struct knet_vty *vty); static int knet_cmd_stop(struct knet_vty *vty); /* peer node */ static int knet_cmd_link(struct knet_vty *vty); static int knet_cmd_no_link(struct knet_vty *vty); /* root node description */ vty_node_cmds_t root_cmds[] = { { "configure", "enter configuration mode", NULL, knet_cmd_config }, { "exit", "exit from CLI", NULL, knet_cmd_logout }, { "help", "display basic help", NULL, knet_cmd_help }, { "logout", "exit from CLI", NULL, knet_cmd_logout }, { "who", "display users connected to CLI", NULL, knet_cmd_who }, { NULL, NULL, NULL, NULL }, }; /* config node description */ vty_param_t no_int_params[] = { { CMDS_PARAM_KNET }, { CMDS_PARAM_NOMORE }, }; vty_node_cmds_t no_config_cmds[] = { { "interface", "destroy kronosnet interface", no_int_params, knet_cmd_no_interface }, { NULL, NULL, NULL, NULL }, }; vty_param_t int_params[] = { { CMDS_PARAM_KNET }, { CMDS_PARAM_NODEID }, { CMDS_PARAM_NOMORE }, }; vty_node_cmds_t config_cmds[] = { { "exit", "exit configuration mode", NULL, knet_cmd_exit_node }, { "interface", "configure kronosnet interface", int_params, knet_cmd_interface }, { "show", "show running config", NULL, knet_cmd_show_conf }, { "help", "display basic help", NULL, knet_cmd_help }, { "logout", "exit from CLI", NULL, knet_cmd_logout }, { "no", "revert command", NULL, NULL }, { "who", "display users connected to CLI", NULL, knet_cmd_who }, { "write", "write current config to file", NULL, knet_cmd_write_conf }, { NULL, NULL, NULL, NULL }, }; /* interface node description */ vty_param_t ip_params[] = { { CMDS_PARAM_IP }, { CMDS_PARAM_IP_PREFIX }, { CMDS_PARAM_NOMORE }, }; vty_param_t peer_params[] = { { CMDS_PARAM_NAME }, { CMDS_PARAM_NODEID }, { CMDS_PARAM_NOMORE }, }; vty_node_cmds_t no_interface_cmds[] = { { "ip", "remove ip address", ip_params, knet_cmd_no_ip }, { "mtu", "revert to default MTU", NULL, knet_cmd_no_mtu }, { "peer", "remove peer from this interface", peer_params, knet_cmd_no_peer }, { NULL, NULL, NULL, NULL }, }; vty_param_t mtu_params[] = { { CMDS_PARAM_MTU }, { CMDS_PARAM_NOMORE }, }; vty_param_t baseport_params[] = { { CMDS_PARAM_IP_PORT }, { CMDS_PARAM_NOMORE }, }; vty_node_cmds_t interface_cmds[] = { { "baseport", "set base listening port for this interface", baseport_params, knet_cmd_baseport }, { "exit", "exit configuration mode", NULL, knet_cmd_exit_node }, { "help", "display basic help", NULL, knet_cmd_help }, { "ip", "add ip address", ip_params, knet_cmd_ip }, { "logout", "exit from CLI", NULL, knet_cmd_logout }, { "mtu", "set mtu", mtu_params, knet_cmd_mtu }, { "no", "revert command", NULL, NULL }, { "peer", "add peer endpoint", peer_params, knet_cmd_peer }, { "show", "show running config", NULL, knet_cmd_show_conf }, { "start", "start forwarding engine", NULL, knet_cmd_start }, { "stop", "stop forwarding engine", NULL, knet_cmd_stop }, { "who", "display users connected to CLI", NULL, knet_cmd_who }, { "write", "write current config to file", NULL, knet_cmd_write_conf }, { NULL, NULL, NULL, NULL }, }; /* peer node description */ vty_param_t link_params[] = { { CMDS_PARAM_IP }, { CMDS_PARAM_NOMORE }, }; vty_node_cmds_t no_peer_cmds[] = { { "link", "remove peer endpoint", link_params, knet_cmd_no_link}, { NULL, NULL, NULL, NULL }, }; vty_node_cmds_t peer_cmds[] = { { "exit", "exit configuration mode", NULL, knet_cmd_exit_node }, { "help", "display basic help", NULL, knet_cmd_help }, { "link", "add peer endpoint", link_params, knet_cmd_link }, { "logout", "exit from CLI", NULL, knet_cmd_logout }, { "no", "revert command", NULL, NULL }, { "show", "show running config", NULL, knet_cmd_show_conf }, { "who", "display users connected to CLI", NULL, knet_cmd_who }, { "write", "write current config to file", NULL, knet_cmd_write_conf }, { NULL, NULL, NULL, NULL }, }; /* link node description */ vty_node_cmds_t link_cmds[] = { { "exit", "exit configuration mode", NULL, knet_cmd_exit_node }, { "help", "display basic help", NULL, knet_cmd_help }, { "logout", "exit from CLI", NULL, knet_cmd_logout }, { "no", "revert command", NULL, NULL }, { "show", "show running config", NULL, knet_cmd_show_conf }, { "who", "display users connected to CLI", NULL, knet_cmd_who }, { "write", "write current config to file", NULL, knet_cmd_write_conf }, { NULL, NULL, NULL, NULL }, }; /* nodes */ vty_nodes_t knet_vty_nodes[] = { { NODE_ROOT, "knet", root_cmds, NULL }, { NODE_CONFIG, "config", config_cmds, no_config_cmds }, { NODE_INTERFACE, "iface", interface_cmds, no_interface_cmds }, { NODE_PEER, "peer", peer_cmds, no_peer_cmds }, { NODE_LINK, "link", link_cmds, NULL }, { -1, NULL, NULL }, }; /* command execution */ /* links */ static int knet_cmd_no_link(struct knet_vty *vty) { struct knet_cfg *knet_iface = (struct knet_cfg *)vty->iface; struct knet_host *host = (struct knet_host *)vty->host; int j, paramlen = 0, paramoffset = 0, found = 0; char *param = NULL; char ipaddr[KNET_MAX_HOST_LEN], port[6]; get_param(vty, 1, ¶m, ¶mlen, ¶moffset); param_to_str(ipaddr, KNET_MAX_HOST_LEN, param, paramlen); memset(port, 0, sizeof(port)); snprintf(port, 6, "%d", knet_iface->cfg_ring.base_port + knet_iface->cfg_eth.node_id); for (j = 0; j < KNET_MAX_LINK; j++) { if (!strcmp(host->link[j].ipaddr, ipaddr) && !strcmp(host->link[j].port, port)) { found = 1; break; } } if (!found) { knet_vty_write(vty, "Error: unable to find link%s", telnet_newline); return -1; } host->link[j].ready = 0; return 0; } static int knet_cmd_link(struct knet_vty *vty) { struct knet_cfg *knet_iface = (struct knet_cfg *)vty->iface; struct knet_host *host = (struct knet_host *)vty->host; struct knet_link *klink = NULL; int j, paramlen = 0, paramoffset = 0, err = 0, found = 0; char *param = NULL; char ipaddr[KNET_MAX_HOST_LEN], port[6]; get_param(vty, 1, ¶m, ¶mlen, ¶moffset); param_to_str(ipaddr, KNET_MAX_HOST_LEN, param, paramlen); memset(port, 0, sizeof(port)); snprintf(port, 6, "%d", knet_iface->cfg_ring.base_port + knet_iface->cfg_eth.node_id); for (j = 0; j < KNET_MAX_LINK; j++) { if ((klink == NULL) && (host->link[j].ready == 0)) { klink = &host->link[j]; } if (!strcmp(host->link[j].ipaddr, ipaddr) && !strcmp(host->link[j].port, port)) { found = 1; break; } } if (!found) { if (!klink) { knet_vty_write(vty, "Error: all links are in use!%s", telnet_newline); return -1; } memset(klink, 0, sizeof(struct knet_link)); memcpy(klink->ipaddr, ipaddr, strlen(ipaddr)); memcpy(klink->port, port, strlen(port)); if (strtoaddr(klink->ipaddr, klink->port, (struct sockaddr *)&klink->address, sizeof(klink->address)) != 0) { knet_vty_write(vty, "Error: unable to convert ip addr to sockaddr!%s", telnet_newline); err = -1; goto out_clean; } klink->sock = host->listener->sock; knet_link_timeout(klink, 1000, 5000, 2048); klink->ready = 1; } vty->link = (void *)klink; vty->node = NODE_LINK; out_clean: if (err < 0) { if (klink) free(klink); } return err; } static int knet_find_host(struct knet_vty *vty, struct knet_host **host, const char *nodename, const int requested_node_id) { struct knet_cfg *knet_iface = (struct knet_cfg *)vty->iface; struct knet_host *head = NULL; int err = 0; *host = NULL; if (knet_host_acquire(knet_iface->cfg_ring.knet_h, &head)) { knet_vty_write(vty, "Error: unable to acquire lock on peer list!%s", telnet_newline); return -1; } while (head != NULL) { if (!strcmp(head->name, nodename)) { if (head->node_id == requested_node_id) { *host = head; err = 1; goto out_clean; } else { knet_vty_write(vty, "Error: requested peer exists with another nodeid%s", telnet_newline); err = -1; goto out_clean; } } else { if (head->node_id == requested_node_id) { knet_vty_write(vty, "Error: requested peer nodeid already exists%s", telnet_newline); err = -1; goto out_clean; } } head = head->next; } out_clean: while (knet_host_release(knet_iface->cfg_ring.knet_h, &head) != 0) { knet_vty_write(vty, "Error: unable to release lock on peer list!%s", telnet_newline); sleep(1); } return err; } static int knet_cmd_no_peer(struct knet_vty *vty) { struct knet_cfg *knet_iface = (struct knet_cfg *)vty->iface; int paramlen = 0, paramoffset = 0, requested_node_id = 0, err = 0; char *param = NULL; struct knet_host *host = NULL; char nodename[KNET_MAX_HOST_LEN]; get_param(vty, 1, ¶m, ¶mlen, ¶moffset); param_to_str(nodename, sizeof(nodename), param, paramlen); get_param(vty, 2, ¶m, ¶mlen, ¶moffset); requested_node_id = param_to_int(param, paramlen); if (requested_node_id == knet_iface->cfg_eth.node_id) { knet_vty_write(vty, "Error: remote peer id cannot be the same as local id%s", telnet_newline); return -1; } err = knet_find_host(vty, &host, nodename, requested_node_id); if (err < 0) goto out_clean; if (err == 0) { knet_vty_write(vty, "Error: peer not found in list%s", telnet_newline); goto out_clean; } if (host->listener) { if (knet_listener_remove(knet_iface->cfg_ring.knet_h, host->listener) == -EBUSY) { knet_vty_write(vty, "Error: unable to remove listener from current peer%s", telnet_newline); err = -1; goto out_clean; } } if (knet_host_remove(knet_iface->cfg_ring.knet_h, requested_node_id) < 0) { knet_vty_write(vty, "Error: unable to remove peer from current config%s", telnet_newline); err = -1; goto out_clean; } out_clean: return err; } static int knet_cmd_peer(struct knet_vty *vty) { struct knet_cfg *knet_iface = (struct knet_cfg *)vty->iface; int paramlen = 0, paramoffset = 0, requested_node_id = 0, err = 0; char *param = NULL; struct knet_host *host, *temp = NULL; struct knet_listener *listener = NULL; char nodename[KNET_MAX_HOST_LEN]; get_param(vty, 1, ¶m, ¶mlen, ¶moffset); param_to_str(nodename, sizeof(nodename), param, paramlen); get_param(vty, 2, ¶m, ¶mlen, ¶moffset); requested_node_id = param_to_int(param, paramlen); if (requested_node_id == knet_iface->cfg_eth.node_id) { knet_vty_write(vty, "Error: remote peer id cannot be the same as local id%s", telnet_newline); return -1; } err = knet_find_host(vty, &host, nodename, requested_node_id); if (err < 0) goto out_clean; if (err == 0) { if (knet_host_add(knet_iface->cfg_ring.knet_h, requested_node_id) < 0) { knet_vty_write(vty, "Error: unable to allocate memory for host struct!%s", telnet_newline); err = -1; goto out_clean; } knet_host_get(knet_iface->cfg_ring.knet_h, requested_node_id, &temp); host = temp; knet_host_release(knet_iface->cfg_ring.knet_h, &temp); memcpy(host->name, nodename, strlen(nodename)); listener = malloc(sizeof(struct knet_listener)); if (!listener) { knet_vty_write(vty, "Error: unable to allocate memory for listener struct!%s", telnet_newline); err = -1; goto out_clean; } memset(listener, 0, sizeof(struct knet_listener)); snprintf(listener->ipaddr, KNET_MAX_HOST_LEN, "::"); snprintf(listener->port, 6, "%d", knet_iface->cfg_ring.base_port + requested_node_id); if (strtoaddr(listener->ipaddr, listener->port, (struct sockaddr *)&listener->address, sizeof(listener->address)) != 0) { knet_vty_write(vty, "Error: unable to convert ip addr to sockaddr!%s", telnet_newline); err = -1; goto out_clean; } if (knet_listener_add(knet_iface->cfg_ring.knet_h, listener) != 0) { knet_vty_write(vty, "Error: unable to start listener!%s", telnet_newline); err = -1; goto out_clean; } host->listener = listener; } vty->host = (void *)host; vty->node = NODE_PEER; out_clean: if (err < 0) { if (listener) free(listener); if (host) knet_host_remove(knet_iface->cfg_ring.knet_h, host->node_id); } return err; } static int active_listeners(struct knet_vty *vty) { struct knet_cfg *knet_iface = (struct knet_cfg *)vty->iface; struct knet_host *head = NULL; int listeners = 0; if (knet_host_acquire(knet_iface->cfg_ring.knet_h, &head)) { knet_vty_write(vty, "Error: unable to acquire lock on peer list!%s", telnet_newline); return -1; } while (head != NULL) { if (head->listener) listeners++; head = head->next; } while (knet_host_release(knet_iface->cfg_ring.knet_h, &head) != 0) { knet_vty_write(vty, "Error: unable to release lock on peer list!%s", telnet_newline); sleep(1); } return listeners; } static int knet_cmd_baseport(struct knet_vty *vty) { struct knet_cfg *knet_iface = (struct knet_cfg *)vty->iface; int paramlen = 0, paramoffset = 0, err = 0; char *param = NULL; get_param(vty, 1, ¶m, ¶mlen, ¶moffset); /* need to check if baseport is in use by other interfaces */ err = active_listeners(vty); if (!err) { knet_iface->cfg_ring.base_port = param_to_int(param, paramlen); } if (err > 0) { knet_vty_write(vty, "Error: cannot switch baseport when listeners are active%s", telnet_newline); err = -1; } return err; } static int knet_cmd_no_ip(struct knet_vty *vty) { int paramlen = 0, paramoffset = 0; char *param = NULL; char ipaddr[KNET_MAX_HOST_LEN], prefix[4]; struct knet_cfg *knet_iface = (struct knet_cfg *)vty->iface; char *error_string = NULL; get_param(vty, 1, ¶m, ¶mlen, ¶moffset); param_to_str(ipaddr, sizeof(ipaddr), param, paramlen); get_param(vty, 2, ¶m, ¶mlen, ¶moffset); param_to_str(prefix, sizeof(prefix), param, paramlen); if (tap_del_ip(knet_iface->cfg_eth.tap, ipaddr, prefix, &error_string) < 0) { knet_vty_write(vty, "Error: Unable to del ip addr %s/%s on device %s%s", ipaddr, prefix, tap_get_name(knet_iface->cfg_eth.tap), telnet_newline); if (error_string) { knet_vty_write(vty, "(%s)%s", error_string, telnet_newline); free(error_string); } return -1; } return 0; } static int knet_cmd_ip(struct knet_vty *vty) { int paramlen = 0, paramoffset = 0; char *param = NULL; char ipaddr[512], prefix[4]; struct knet_cfg *knet_iface = (struct knet_cfg *)vty->iface; char *error_string = NULL; get_param(vty, 1, ¶m, ¶mlen, ¶moffset); param_to_str(ipaddr, sizeof(ipaddr), param, paramlen); get_param(vty, 2, ¶m, ¶mlen, ¶moffset); param_to_str(prefix, sizeof(prefix), param, paramlen); if (tap_add_ip(knet_iface->cfg_eth.tap, ipaddr, prefix, &error_string) < 0) { knet_vty_write(vty, "Error: Unable to set ip addr %s/%s on device %s%s", ipaddr, prefix, tap_get_name(knet_iface->cfg_eth.tap), telnet_newline); if (error_string) { knet_vty_write(vty, "(%s)%s", error_string, telnet_newline); free(error_string); } return -1; } return 0; } static int knet_cmd_no_mtu(struct knet_vty *vty) { struct knet_cfg *knet_iface = (struct knet_cfg *)vty->iface; if (tap_reset_mtu(knet_iface->cfg_eth.tap) < 0) { knet_vty_write(vty, "Error: Unable to set default mtu on device %s%s", tap_get_name(knet_iface->cfg_eth.tap), telnet_newline); return -1; } return 0; } static int knet_cmd_mtu(struct knet_vty *vty) { struct knet_cfg *knet_iface = (struct knet_cfg *)vty->iface; int paramlen = 0, paramoffset = 0, expected_mtu = 0; char *param = NULL; get_param(vty, 1, ¶m, ¶mlen, ¶moffset); expected_mtu = param_to_int(param, paramlen); if (tap_set_mtu(knet_iface->cfg_eth.tap, expected_mtu) < 0) { knet_vty_write(vty, "Error: Unable to set requested mtu %d on device %s%s", expected_mtu, tap_get_name(knet_iface->cfg_eth.tap), telnet_newline); return -1; } return 0; } static int knet_cmd_stop(struct knet_vty *vty) { struct knet_cfg *knet_iface = (struct knet_cfg *)vty->iface; char *error_down = NULL, *error_postdown = NULL; int err = 0; err = tap_set_down(knet_iface->cfg_eth.tap, &error_down, &error_postdown); if (err < 0) { knet_vty_write(vty, "Error: Unable to set interface %s down!%s", tap_get_name(knet_iface->cfg_eth.tap), telnet_newline); } else { knet_handle_setfwd(knet_iface->cfg_ring.knet_h, 0); knet_iface->active = 0; } if (error_down) { knet_vty_write(vty, "down script output:%s(%s)%s", telnet_newline, error_down, telnet_newline); free(error_down); } if (error_postdown) { knet_vty_write(vty, "post-down script output:%s(%s)%s", telnet_newline, error_postdown, telnet_newline); free(error_postdown); } return err; } static int knet_cmd_start(struct knet_vty *vty) { struct knet_cfg *knet_iface = (struct knet_cfg *)vty->iface; char *error_preup = NULL, *error_up = NULL; int err = 0; err = tap_set_up(knet_iface->cfg_eth.tap, &error_preup, &error_up); if (err < 0) { knet_vty_write(vty, "Error: Unable to set interface %s up!%s", tap_get_name(knet_iface->cfg_eth.tap), telnet_newline); knet_handle_setfwd(knet_iface->cfg_ring.knet_h, 0); } else { knet_handle_setfwd(knet_iface->cfg_ring.knet_h, 1); knet_iface->active = 1; } if (error_preup) { knet_vty_write(vty, "pre-up script output:%s(%s)%s", telnet_newline, error_preup, telnet_newline); free(error_preup); } if (error_up) { knet_vty_write(vty, "up script output:%s(%s)%s", telnet_newline, error_up, telnet_newline); free(error_up); } return err; } static int knet_cmd_no_interface(struct knet_vty *vty) { int err = 0, paramlen = 0, paramoffset = 0; char *param = NULL; char device[IFNAMSIZ]; struct knet_cfg *knet_iface = NULL; struct knet_host *host; char *ip_list = NULL; int ip_list_entries = 0, i, offset = 0; char *error_string = NULL; get_param(vty, 1, ¶m, ¶mlen, ¶moffset); param_to_str(device, IFNAMSIZ, param, paramlen); knet_iface = knet_get_iface(device, 0); if (!knet_iface) { knet_vty_write(vty, "Error: Unable to find requested interface%s", telnet_newline); return -1; } vty->iface = (void *)knet_iface; tap_get_ips(knet_iface->cfg_eth.tap, &ip_list, &ip_list_entries); if ((ip_list) && (ip_list_entries > 0)) { for (i = 1; i <= ip_list_entries; i++) { tap_del_ip(knet_iface->cfg_eth.tap, ip_list + offset, ip_list + offset + strlen(ip_list + offset) + 1, &error_string); if (error_string) { free(error_string); error_string = NULL; } offset = offset + strlen(ip_list) + 1; offset = offset + strlen(ip_list + offset) + 1; } free(ip_list); ip_list = NULL; ip_list_entries = 0; } while (1) { struct knet_host *head; while (knet_host_acquire(knet_iface->cfg_ring.knet_h, &head) != 0) { log_error("CLI ERROR: unable to acquire peer lock.. will retry in 1 sec"); sleep (1); } if (head == NULL) break; host = head; while (knet_host_release(knet_iface->cfg_ring.knet_h, &head) != 0) { log_error("CLI ERROR: unable to release peer lock.. will retry in 1 sec"); sleep (1); } for (i = 0; i < KNET_MAX_LINK; i++) host->link[i].ready = 0; knet_listener_remove(knet_iface->cfg_ring.knet_h, host->listener); while (knet_host_remove(knet_iface->cfg_ring.knet_h, host->node_id) != 0) { log_error("CLI ERROR: unable to release peer.. will retry in 1 sec"); sleep (1); } } knet_cmd_stop(vty); if (knet_iface->cfg_ring.knet_h) knet_handle_free(knet_iface->cfg_ring.knet_h); if (knet_iface->cfg_eth.tap) tap_close(knet_iface->cfg_eth.tap); if (knet_iface) knet_destroy_iface(knet_iface); return err; } static int knet_cmd_interface(struct knet_vty *vty) { int err = 0, paramlen = 0, paramoffset = 0, found = 0, requested_id; char *param = NULL, *cur_mac = NULL; char device[IFNAMSIZ]; char mac[18]; int maclen; struct knet_cfg *knet_iface = NULL; get_param(vty, 1, ¶m, ¶mlen, ¶moffset); param_to_str(device, IFNAMSIZ, param, paramlen); get_param(vty, 2, ¶m, ¶mlen, ¶moffset); requested_id = param_to_int(param, paramlen); knet_iface = knet_get_iface(device, 1); if (!knet_iface) { knet_vty_write(vty, "Error: Unable to allocate memory for config structures%s", telnet_newline); return -1; } if (knet_iface->cfg_eth.tap) { found = 1; goto tap_found; } if (!knet_iface->cfg_eth.tap) knet_iface->cfg_eth.tap = tap_open(device, IFNAMSIZ, DEFAULT_CONFIG_DIR); if ((!knet_iface->cfg_eth.tap) && (errno = EBUSY)) { knet_vty_write(vty, "Error: interface %s seems to exist in the system%s", device, telnet_newline); err = -1; goto out_clean; } if (!knet_iface->cfg_eth.tap) { knet_vty_write(vty, "Error: Unable to create %s system tap device%s", device, telnet_newline); err = -1; goto out_clean; } tap_found: if (knet_iface->cfg_ring.knet_h) goto knet_found; knet_iface->cfg_ring.knet_h = knet_handle_new(tap_get_fd(knet_iface->cfg_eth.tap), requested_id); if (!knet_iface->cfg_ring.knet_h) { knet_vty_write(vty, "Error: Unable to create ring handle for device %s%s", device, telnet_newline); err = -1; goto out_clean; } knet_found: if (found) { if (requested_id == knet_iface->cfg_eth.node_id) goto out_found; knet_vty_write(vty, "Error: no interface %s with nodeid %d found%s", device, requested_id, telnet_newline); goto out_clean; } else { knet_iface->cfg_eth.node_id = requested_id; } if (tap_get_mac(knet_iface->cfg_eth.tap, &cur_mac) < 0) { knet_vty_write(vty, "Error: Unable to get mac address on device %s%s", device, telnet_newline); err = -1; goto out_clean; } memset(&mac, 0, sizeof(mac)); maclen = strrchr(cur_mac, ':') - cur_mac + 1; memcpy(mac, cur_mac, maclen); snprintf(mac + maclen, sizeof(mac) - maclen, "%x", knet_iface->cfg_eth.node_id); free(cur_mac); if (tap_set_mac(knet_iface->cfg_eth.tap, mac) < 0) { knet_vty_write(vty, "Error: Unable to set mac address %s on device %s%s", mac, device, telnet_newline); err = -1; goto out_clean; } out_found: vty->node = NODE_INTERFACE; vty->iface = (void *)knet_iface; out_clean: if (err) { if (knet_iface->cfg_ring.knet_h) knet_handle_free(knet_iface->cfg_ring.knet_h); if (knet_iface->cfg_eth.tap) tap_close(knet_iface->cfg_eth.tap); knet_destroy_iface(knet_iface); } return err; } static int knet_cmd_exit_node(struct knet_vty *vty) { knet_vty_exit_node(vty); return 0; } static int knet_cmd_print_conf(struct knet_vty *vty) { int i; struct knet_cfg *knet_iface = knet_cfg_head.knet_cfg; struct knet_host *host = NULL; const char *nl = telnet_newline; char *ip_list = NULL; int ip_list_entries = 0, offset = 0; if (vty->filemode) nl = file_newline; knet_vty_write(vty, "configure%s", nl); while (knet_iface != NULL) { knet_vty_write(vty, " interface %s %d%s", tap_get_name(knet_iface->cfg_eth.tap), knet_iface->cfg_eth.node_id, nl); knet_vty_write(vty, " mtu %d%s", tap_get_mtu(knet_iface->cfg_eth.tap), nl); tap_get_ips(knet_iface->cfg_eth.tap, &ip_list, &ip_list_entries); if ((ip_list) && (ip_list_entries > 0)) { for (i = 1; i <= ip_list_entries; i++) { knet_vty_write(vty, " ip %s %s%s", ip_list + offset, ip_list + offset + strlen(ip_list + offset) + 1, nl); offset = offset + strlen(ip_list) + 1; offset = offset + strlen(ip_list + offset) + 1; } free(ip_list); ip_list = NULL; ip_list_entries = 0; } knet_vty_write(vty, " baseport %d%s", knet_iface->cfg_ring.base_port, nl); while (knet_host_acquire(knet_iface->cfg_ring.knet_h, &host)) { log_error("CLI ERROR: waiting for peer lock"); sleep(1); } while (host != NULL) { knet_vty_write(vty, " peer %s %d%s", host->name, host->node_id, nl); for (i = 0; i < KNET_MAX_LINK; i++) { if (host->link[i].ready == 1) { knet_vty_write(vty, " link %s%s", host->link[i].ipaddr, nl); /* print link properties */ knet_vty_write(vty, " exit%s", nl); } } knet_vty_write(vty, " exit%s", nl); host = host->next; } while (knet_host_release(knet_iface->cfg_ring.knet_h, &host) != 0) { log_error("CLI ERROR: unable to release peer lock.. will retry in 1 sec"); sleep(1); } if (knet_iface->active) knet_vty_write(vty, " start%s", nl); knet_vty_write(vty, " exit%s", nl); knet_iface = knet_iface->next; } knet_vty_write(vty, " exit%sexit%s", nl, nl); return 0; } static int knet_cmd_show_conf(struct knet_vty *vty) { return knet_cmd_print_conf(vty); } static int knet_cmd_write_conf(struct knet_vty *vty) { int fd = 1, vty_sock, err = 0, backup = 1; char tempfile[PATH_MAX]; memset(tempfile, 0, sizeof(tempfile)); snprintf(tempfile, sizeof(tempfile), "%s.sav", knet_cfg_head.conffile); err = rename(knet_cfg_head.conffile, tempfile); if ((err < 0) && (errno != ENOENT)) { knet_vty_write(vty, "Unable to create backup config file %s %s", tempfile, telnet_newline); return -1; } if ((err < 0) && (errno == ENOENT)) backup = 0; fd = open(knet_cfg_head.conffile, O_RDWR | O_CREAT | O_EXCL | O_TRUNC, S_IRUSR | S_IWUSR); if (fd < 0) { knet_vty_write(vty, "Error unable to open file%s", telnet_newline); return -1; } vty_sock = vty->vty_sock; vty->vty_sock = fd; vty->filemode = 1; knet_cmd_print_conf(vty); vty->vty_sock = vty_sock; vty->filemode = 0; close(fd); knet_vty_write(vty, "Configuration saved to %s%s", knet_cfg_head.conffile, telnet_newline); if (backup) knet_vty_write(vty, "Old configuration file has been stored in %s%s", tempfile, telnet_newline); return err; } static int knet_cmd_config(struct knet_vty *vty) { int err = 0; if (!vty->user_can_enable) { knet_vty_write(vty, "Error: user %s does not have enough privileges to perform config operations%s", vty->username, telnet_newline); return -1; } pthread_mutex_lock(&knet_vty_mutex); if (knet_vty_config >= 0) { knet_vty_write(vty, "Error: configuration is currently locked by user %s on vty(%d). Try again later%s", vty->username, knet_vty_config, telnet_newline); err = -1; goto out_clean; } vty->node = NODE_CONFIG; knet_vty_config = vty->conn_num; out_clean: pthread_mutex_unlock(&knet_vty_mutex); return err; } static int knet_cmd_logout(struct knet_vty *vty) { vty->got_epipe = 1; return 0; } static int knet_cmd_who(struct knet_vty *vty) { int conn_index; pthread_mutex_lock(&knet_vty_mutex); for(conn_index = 0; conn_index < KNET_VTY_TOTAL_MAX_CONN; conn_index++) { if (knet_vtys[conn_index].active) { knet_vty_write(vty, "User %s connected on vty(%d) from %s%s", knet_vtys[conn_index].username, knet_vtys[conn_index].conn_num, knet_vtys[conn_index].ip, telnet_newline); } } pthread_mutex_unlock(&knet_vty_mutex); return 0; } static int knet_cmd_help(struct knet_vty *vty) { knet_vty_write(vty, PACKAGE " VTY provides advanced help feature.%s%s" "When you need help, anytime at the command line please press '?'.%s%s" "If nothing matches, the help list will be empty and you must backup%s" " until entering a '?' shows the available options.%s", telnet_newline, telnet_newline, telnet_newline, telnet_newline, telnet_newline, telnet_newline); return 0; } /* exported API to vty_cli.c */ int knet_vty_execute_cmd(struct knet_vty *vty) { const vty_node_cmds_t *cmds = NULL; char *cmd = NULL; int cmdlen = 0; int cmdoffset = 0; if (knet_vty_is_line_empty(vty)) return 0; cmds = get_cmds(vty, &cmd, &cmdlen, &cmdoffset); /* this will eventually disappear. keep it as safeguard for now */ if (cmds == NULL) { knet_vty_write(vty, "No commands associated to this node%s", telnet_newline); return 0; } return match_command(vty, cmds, cmd, cmdlen, cmdoffset, KNET_VTY_MATCH_EXEC); } int knet_read_conf(void) { int err = 0, len = 0, line = 0; struct knet_vty *vty = &knet_vtys[0]; FILE *file = NULL; file = fopen(knet_cfg_head.conffile, "r"); if ((file == NULL) && (errno != ENOENT)) { log_error("Unable to open config file for reading %s", knet_cfg_head.conffile); return -1; } if ((file == NULL) && (errno == ENOENT)) { log_info("Configuration file %s not found, starting with default empty config", knet_cfg_head.conffile); return 0; } vty->vty_sock = 1; vty->user_can_enable = 1; vty->filemode = 1; while(fgets(vty->line, sizeof(vty->line), file) != NULL) { line++; len = strlen(vty->line) - 1; memset(&vty->line[len], 0, 1); vty->line_idx = len; err = knet_vty_execute_cmd(vty); if (err != 0) { log_error("line[%d]: %s", line, vty->line); break; } } fclose(file); memset(vty, 0, sizeof(vty)); return err; } void knet_vty_help(struct knet_vty *vty) { int idx = 0; const vty_node_cmds_t *cmds = NULL; char *cmd = NULL; int cmdlen = 0; int cmdoffset = 0; cmds = get_cmds(vty, &cmd, &cmdlen, &cmdoffset); /* this will eventually disappear. keep it as safeguard for now */ if (cmds == NULL) { knet_vty_write(vty, "No commands associated to this node%s", telnet_newline); return; } if (knet_vty_is_line_empty(vty) || cmd == NULL) { while (cmds[idx].cmd != NULL) { print_help(vty, cmds, idx); idx++; } return; } match_command(vty, cmds, cmd, cmdlen, cmdoffset, KNET_VTY_MATCH_HELP); } void knet_vty_tab_completion(struct knet_vty *vty) { const vty_node_cmds_t *cmds = NULL; char *cmd = NULL; int cmdlen = 0; int cmdoffset = 0; if (knet_vty_is_line_empty(vty)) return; knet_vty_write(vty, "%s", telnet_newline); cmds = get_cmds(vty, &cmd, &cmdlen, &cmdoffset); /* this will eventually disappear. keep it as safeguard for now */ if (cmds == NULL) { knet_vty_write(vty, "No commands associated to this node%s", telnet_newline); return; } match_command(vty, cmds, cmd, cmdlen, cmdoffset, KNET_VTY_MATCH_EXPAND); knet_vty_prompt(vty); knet_vty_write(vty, "%s", vty->line); } diff --git a/libvty/vty_utils.c b/libvty/vty_utils.c index d46a013d..0befc1da 100644 --- a/libvty/vty_utils.c +++ b/libvty/vty_utils.c @@ -1,258 +1,258 @@ #include "config.h" #include #include #include #include #include #include #include #include #include -#include "utils.h" +#include "logging.h" #include "vty_cli.h" #include "vty_cli_cmds.h" #include "vty_utils.h" static int check_vty(struct knet_vty *vty) { if (!vty) { errno = EINVAL; return -1; } if (vty->got_epipe) { errno = EPIPE; return -1; } return 0; } /* * TODO: implement loopy_write here * should sock be non-blocking? */ static int knet_vty_loopy_write(struct knet_vty *vty, const char *buf, size_t bufsize) { ssize_t writelen; writelen = write(vty->vty_sock, buf, bufsize); if (writelen < 0) vty->got_epipe = 1; return writelen; } int knet_vty_write(struct knet_vty *vty, const char *format, ...) { va_list args; int len = 0; char buf[VTY_MAX_BUFFER_SIZE]; if (check_vty(vty)) return -1; va_start (args, format); len = vsnprintf (buf, VTY_MAX_BUFFER_SIZE, format, args); va_end (args); if ((len < 0) || (len > VTY_MAX_BUFFER_SIZE)) return -1; return knet_vty_loopy_write(vty, buf, len); } static int knet_vty_read_real(struct knet_vty *vty, unsigned char *buf, size_t bufsize, int ignore_iac) { ssize_t readlen; iac_retry: readlen = recv(vty->vty_sock, buf, bufsize, 0); if (readlen == 0) { vty->got_epipe = 1; goto out_clean; } if (readlen < 0) goto out_clean; vty->idle = 0; /* at somepoint we have to add IAC parsing */ if ((buf[0] == IAC) && (ignore_iac)) goto iac_retry; out_clean: return readlen; } int knet_vty_read(struct knet_vty *vty, unsigned char *buf, size_t bufsize) { if (check_vty(vty)) return -1; if ((!buf) || (bufsize == 0)) { errno = EINVAL; return -1; } return knet_vty_read_real(vty, buf, bufsize, 1); } static int knet_vty_set_echooff(struct knet_vty *vty) { unsigned char cmdreply[VTY_MAX_BUFFER_SIZE]; unsigned char cmdechooff[] = { IAC, WILL, TELOPT_ECHO, '\0' }; unsigned char cmdechooffreply[] = { IAC, DO, TELOPT_ECHO, '\0' }; ssize_t readlen; if (knet_vty_write(vty, "%s", cmdechooff) < 0) return -1; readlen = knet_vty_read_real(vty, cmdreply, VTY_MAX_BUFFER_SIZE, 0); if (readlen < 0) return readlen; if (memcmp(&cmdreply, &cmdechooffreply, readlen)) return -1; return 0; } static int knet_vty_set_echoon(struct knet_vty *vty) { unsigned char cmdreply[VTY_MAX_BUFFER_SIZE]; unsigned char cmdechoon[] = { IAC, WONT, TELOPT_ECHO, '\0' }; unsigned char cmdechoonreply[] = { IAC, DONT, TELOPT_ECHO, '\0' }; ssize_t readlen; if (knet_vty_write(vty, "%s", cmdechoon) < 0) return -1; readlen = knet_vty_read_real(vty, cmdreply, VTY_MAX_BUFFER_SIZE, 0); if (readlen < 0) return readlen; if (memcmp(&cmdreply, &cmdechoonreply, readlen)) return -1; return 0; } int knet_vty_set_echo(struct knet_vty *vty, int on) { if (check_vty(vty)) return -1; if (on) return knet_vty_set_echoon(vty); return knet_vty_set_echooff(vty); } void knet_vty_print_banner(struct knet_vty *vty) { if (check_vty(vty)) return; knet_vty_write(vty, "Welcome to " PACKAGE " " PACKAGE_VERSION " (built " __DATE__ " " __TIME__ ")\n"); } int knet_vty_set_iacs(struct knet_vty *vty) { unsigned char cmdreply[VTY_MAX_BUFFER_SIZE]; unsigned char cmdsga[] = { IAC, WILL, TELOPT_SGA, '\0' }; unsigned char cmdsgareply[] = { IAC, DO, TELOPT_SGA, '\0' }; unsigned char cmdlm[] = { IAC, DONT, TELOPT_LINEMODE, '\0' }; ssize_t readlen; if (check_vty(vty)) return -1; if (knet_vty_set_echo(vty, 0) < 0) return -1; if (knet_vty_write(vty, "%s", cmdsga) < 0) return -1; readlen = knet_vty_read_real(vty, cmdreply, VTY_MAX_BUFFER_SIZE, 0); if (readlen < 0) return readlen; if (memcmp(&cmdreply, &cmdsgareply, readlen)) return -1; if (knet_vty_write(vty, "%s", cmdlm) < 0) return -1; return 0; } void knet_vty_free_history(struct knet_vty *vty) { int i; if (check_vty(vty)) return; for (i = 0; i < KNET_VTY_MAX_HIST; i++) { if (vty->history[i]) { free(vty->history[i]); vty->history[i] = NULL; } } } void knet_vty_exit_node(struct knet_vty *vty) { switch(vty->node) { case NODE_LINK: vty->node = NODE_PEER; break; case NODE_PEER: vty->node = NODE_INTERFACE; break; case NODE_INTERFACE: vty->node = NODE_CONFIG; break; case NODE_CONFIG: pthread_mutex_lock(&knet_vty_mutex); knet_vty_config = -1; pthread_mutex_unlock(&knet_vty_mutex); vty->node = NODE_ROOT; break; case NODE_ROOT: vty->got_epipe = 1; break; default: knet_vty_write(vty, "No idea where to go..%s", telnet_newline); break; } } int knet_vty_is_line_empty(struct knet_vty *vty) { int idx; for (idx = 0; idx < vty->line_idx; idx++) { if (vty->line[idx] != ' ') return 0; } return 1; } void knet_vty_prompt(struct knet_vty *vty) { char buf[3]; if (vty->user_can_enable) { buf[0] = '#'; } else { buf[0] = '>'; } buf[1] = ' '; buf[2] = 0; knet_vty_write(vty, "%s%s", knet_vty_nodes[vty->node].prompt, buf); }