summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/command.c3
-rw-r--r--lib/command.h1
-rw-r--r--lib/gitversion.pl2
-rw-r--r--lib/grammar_sandbox_main.c5
-rw-r--r--lib/libfrr.c290
-rw-r--r--lib/libfrr.h1
-rw-r--r--lib/libospf.h2
-rw-r--r--lib/log.c66
-rw-r--r--lib/log.h3
-rw-r--r--lib/monotime.h4
-rw-r--r--lib/privs.c17
-rw-r--r--lib/privs.h1
-rw-r--r--lib/pw.h52
-rw-r--r--lib/subdir.am21
-rw-r--r--lib/thread.c12
-rw-r--r--lib/vty.c142
-rw-r--r--lib/vty.h7
-rw-r--r--lib/zclient.c68
-rw-r--r--lib/zclient.h37
19 files changed, 629 insertions, 105 deletions
diff --git a/lib/command.c b/lib/command.c
index f28a55ec6d..09ffa6ce56 100644
--- a/lib/command.c
+++ b/lib/command.c
@@ -110,6 +110,7 @@ const char *node_names[] = {
"forwarding", // FORWARDING_NODE,
"protocol", // PROTOCOL_NODE,
"mpls", // MPLS_NODE,
+ "pw", // PW_NODE,
"vty", // VTY_NODE,
"link-params", // LINK_PARAMS_NODE,
"bgp evpn vni", // BGP_EVPN_VNI_NODE,
@@ -1253,6 +1254,7 @@ void cmd_exit(struct vty *vty)
vty_config_unlock(vty);
break;
case INTERFACE_NODE:
+ case PW_NODE:
case NS_NODE:
case VRF_NODE:
case ZEBRA_NODE:
@@ -1338,6 +1340,7 @@ DEFUN (config_end,
break;
case CONFIG_NODE:
case INTERFACE_NODE:
+ case PW_NODE:
case NS_NODE:
case VRF_NODE:
case ZEBRA_NODE:
diff --git a/lib/command.h b/lib/command.h
index 5184b53a9f..d0c9f0eaf9 100644
--- a/lib/command.h
+++ b/lib/command.h
@@ -132,6 +132,7 @@ enum node_type {
FORWARDING_NODE, /* IP forwarding node. */
PROTOCOL_NODE, /* protocol filtering node */
MPLS_NODE, /* MPLS config node */
+ PW_NODE, /* Pseudowire config node */
VTY_NODE, /* Vty node. */
LINK_PARAMS_NODE, /* Link-parameters node */
BGP_EVPN_VNI_NODE, /* BGP EVPN VNI */
diff --git a/lib/gitversion.pl b/lib/gitversion.pl
index 8ddd9ffa55..2718046d0b 100644
--- a/lib/gitversion.pl
+++ b/lib/gitversion.pl
@@ -4,7 +4,7 @@ use strict;
my $dir = shift;
chdir $dir || die "$dir: $!\n";
-my $gitdesc = `git describe --always --dirty || echo -- \"0-gUNKNOWN\"`;
+my $gitdesc = `git describe --always --first-parent --tags --dirty --match 'frr-*' || echo -- \"0-gUNKNOWN\"`;
chomp $gitdesc;
my $gitsuffix = ($gitdesc =~ /([0-9a-fA-F]{7}(-dirty)?)$/) ? "-g$1" : "-gUNKNOWN";
diff --git a/lib/grammar_sandbox_main.c b/lib/grammar_sandbox_main.c
index c236d2c7be..89b0993d1d 100644
--- a/lib/grammar_sandbox_main.c
+++ b/lib/grammar_sandbox_main.c
@@ -26,10 +26,11 @@
#include "command.h"
#include "memory_vty.h"
-static void vty_do_exit(void)
+static void vty_do_exit(int isexit)
{
printf("\nend.\n");
- exit(0);
+ if (!isexit)
+ exit(0);
}
struct thread_master *master;
diff --git a/lib/libfrr.c b/lib/libfrr.c
index c901dcc229..e5573da900 100644
--- a/lib/libfrr.c
+++ b/lib/libfrr.c
@@ -21,8 +21,12 @@
#include <zebra.h>
#include <sys/un.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+
#include "libfrr.h"
#include "getopt.h"
+#include "privs.h"
#include "vty.h"
#include "command.h"
#include "version.h"
@@ -30,6 +34,7 @@
#include "zclient.h"
#include "log_int.h"
#include "module.h"
+#include "network.h"
DEFINE_HOOK(frr_late_init, (struct thread_master * tm), (tm))
@@ -93,12 +98,15 @@ static const struct option lo_cfg_pid_dry[] = {
{"pid_file", required_argument, NULL, 'i'},
{"config_file", required_argument, NULL, 'f'},
{"dryrun", no_argument, NULL, 'C'},
+ {"terminal", no_argument, NULL, 't'},
{NULL}};
static const struct optspec os_cfg_pid_dry = {
- "f:i:C",
+ "f:i:Ct",
" -f, --config_file Set configuration file name\n"
" -i, --pid_file Set process identifier file name\n"
- " -C, --dryrun Check configuration for validity and exit\n",
+ " -C, --dryrun Check configuration for validity and exit\n"
+ " -t, --terminal Open terminal session on stdio\n"
+ " -d -t Daemonize after terminal session ends\n",
lo_cfg_pid_dry};
@@ -344,6 +352,11 @@ static int frr_opt(int opt)
return 1;
di->dryrun = 1;
break;
+ case 't':
+ if (di->flags & FRR_NO_CFG_PID_DRY)
+ return 1;
+ di->terminal = 1;
+ break;
case 'z':
if (di->flags & FRR_NO_ZCLIENT)
return 1;
@@ -434,6 +447,49 @@ int frr_getopt(int argc, char *const argv[], int *longindex)
return opt;
}
+static void frr_mkdir(const char *path, bool strip)
+{
+ char buf[256];
+ mode_t prev;
+ int ret;
+ struct zprivs_ids_t ids;
+
+ if (strip) {
+ char *slash = strrchr(path, '/');
+ size_t plen;
+ if (!slash)
+ return;
+ plen = slash - path;
+ if (plen > sizeof(buf) - 1)
+ return;
+ memcpy(buf, path, plen);
+ buf[plen] = '\0';
+ path = buf;
+ }
+
+ /* o+rx (..5) is needed for the frrvty group to work properly;
+ * without it, users in the frrvty group can't access the vty sockets.
+ */
+ prev = umask(0022);
+ ret = mkdir(path, 0755);
+ umask(prev);
+
+ if (ret != 0) {
+ /* if EEXIST, return without touching the permissions,
+ * so user-set custom permissions are left in place
+ */
+ if (errno == EEXIST)
+ return;
+
+ zlog_warn("failed to mkdir \"%s\": %s", path, strerror(errno));
+ return;
+ }
+
+ zprivs_get_ids(&ids);
+ if (chown(path, ids.uid_normal, ids.gid_normal))
+ zlog_warn("failed to chown \"%s\": %s", path, strerror(errno));
+}
+
static struct thread_master *master;
struct thread_master *frr_init(void)
{
@@ -449,6 +505,8 @@ struct thread_master *frr_init(void)
snprintf(frr_protonameinst, sizeof(frr_protonameinst), "%s[%u]",
di->logname, di->instance);
+ zprivs_preinit(di->privs);
+
openzlog(di->progname, di->logname, di->instance,
LOG_CONS | LOG_NDELAY | LOG_PID, LOG_DAEMON);
#if defined(HAVE_CUMULUS)
@@ -462,6 +520,16 @@ struct thread_master *frr_init(void)
exit(1);
}
+ /* don't mkdir these as root... */
+ if (!(di->flags & FRR_NO_PRIVSEP)) {
+ if (!di->pid_file || !di->vty_path)
+ frr_mkdir(frr_vtydir, false);
+ if (di->pid_file)
+ frr_mkdir(di->pid_file, true);
+ if (di->vty_path)
+ frr_mkdir(di->vty_path, true);
+ }
+
frrmod_init(di->module);
while (modules) {
modules = (oc = modules)->next;
@@ -488,6 +556,134 @@ struct thread_master *frr_init(void)
return master;
}
+static int rcvd_signal = 0;
+
+static void rcv_signal(int signum)
+{
+ rcvd_signal = signum;
+ /* poll() is interrupted by the signal; handled below */
+}
+
+static void frr_daemon_wait(int fd)
+{
+ struct pollfd pfd[1];
+ int ret;
+ pid_t exitpid;
+ int exitstat;
+ sigset_t sigs, prevsigs;
+
+ sigemptyset(&sigs);
+ sigaddset(&sigs, SIGTSTP);
+ sigaddset(&sigs, SIGQUIT);
+ sigaddset(&sigs, SIGINT);
+ sigprocmask(SIG_BLOCK, &sigs, &prevsigs);
+
+ struct sigaction sa = {
+ .sa_handler = rcv_signal, .sa_flags = SA_RESETHAND,
+ };
+ sigemptyset(&sa.sa_mask);
+ sigaction(SIGTSTP, &sa, NULL);
+ sigaction(SIGQUIT, &sa, NULL);
+ sigaction(SIGINT, &sa, NULL);
+
+ do {
+ char buf[1];
+ ssize_t nrecv;
+
+ pfd[0].fd = fd;
+ pfd[0].events = POLLIN;
+
+ rcvd_signal = 0;
+
+#if defined(HAVE_PPOLL)
+ ret = ppoll(pfd, 1, NULL, &prevsigs);
+#elif defined(HAVE_POLLTS)
+ ret = pollts(pfd, 1, NULL, &prevsigs);
+#else
+ /* racy -- only used on FreeBSD 9 */
+ sigset_t tmpsigs;
+ sigprocmask(SIG_SETMASK, &prevsigs, &tmpsigs);
+ ret = poll(pfd, 1, -1);
+ sigprocmask(SIG_SETMASK, &tmpsigs, NULL);
+#endif
+ if (ret < 0 && errno != EINTR && errno != EAGAIN) {
+ perror("poll()");
+ exit(1);
+ }
+ switch (rcvd_signal) {
+ case SIGTSTP:
+ send(fd, "S", 1, 0);
+ do {
+ nrecv = recv(fd, buf, sizeof(buf), 0);
+ } while (nrecv == -1
+ && (errno == EINTR || errno == EAGAIN));
+
+ raise(SIGTSTP);
+ sigaction(SIGTSTP, &sa, NULL);
+ send(fd, "R", 1, 0);
+ break;
+ case SIGINT:
+ send(fd, "I", 1, 0);
+ break;
+ case SIGQUIT:
+ send(fd, "Q", 1, 0);
+ break;
+ }
+ } while (ret <= 0);
+
+ exitpid = waitpid(-1, &exitstat, WNOHANG);
+ if (exitpid == 0)
+ /* child successfully went to main loop & closed socket */
+ exit(0);
+
+ /* child failed one way or another ... */
+ if (WIFEXITED(exitstat))
+ fprintf(stderr, "%s failed to start, exited %d\n", di->name,
+ WEXITSTATUS(exitstat));
+ else if (WIFSIGNALED(exitstat))
+ fprintf(stderr, "%s crashed in startup, signal %d\n", di->name,
+ WTERMSIG(exitstat));
+ else
+ fprintf(stderr, "%s failed to start, unknown problem\n",
+ di->name);
+ exit(1);
+}
+
+static int daemon_ctl_sock = -1;
+
+static void frr_daemonize(void)
+{
+ int fds[2];
+ pid_t pid;
+
+ if (socketpair(AF_UNIX, SOCK_STREAM, 0, fds)) {
+ perror("socketpair() for daemon control");
+ exit(1);
+ }
+ set_cloexec(fds[0]);
+ set_cloexec(fds[1]);
+
+ pid = fork();
+ if (pid < 0) {
+ perror("fork()");
+ exit(1);
+ }
+ if (pid == 0) {
+ /* child */
+ close(fds[0]);
+ if (setsid() < 0) {
+ perror("setsid()");
+ exit(1);
+ }
+
+ daemon_ctl_sock = fds[1];
+ return;
+ }
+
+ close(fds[1]);
+ frr_daemon_wait(fds[0]);
+}
+
void frr_config_fork(void)
{
hook_call(frr_late_init, master);
@@ -506,11 +702,8 @@ void frr_config_fork(void)
if (di->dryrun)
exit(0);
- /* Daemonize. */
- if (di->daemon_mode && daemon(0, 0) < 0) {
- zlog_err("Zebra daemon failed: %s", strerror(errno));
- exit(1);
- }
+ if (di->daemon_mode || di->terminal)
+ frr_daemonize();
if (!di->pid_file)
di->pid_file = pidfile_default;
@@ -538,6 +731,67 @@ void frr_vty_serv(void)
vty_serv_sock(di->vty_addr, di->vty_port, di->vty_path);
}
+static void frr_terminal_close(int isexit)
+{
+ if (daemon_ctl_sock != -1) {
+ close(daemon_ctl_sock);
+ daemon_ctl_sock = -1;
+ }
+
+ if (!di->daemon_mode || isexit) {
+ printf("\n%s exiting\n", di->name);
+ if (!isexit)
+ raise(SIGINT);
+ return;
+ } else {
+ printf("\n%s daemonizing\n", di->name);
+ fflush(stdout);
+ }
+
+ int nullfd = open("/dev/null", O_RDONLY | O_NOCTTY);
+ dup2(nullfd, 0);
+ dup2(nullfd, 1);
+ dup2(nullfd, 2);
+ close(nullfd);
+}
+
+static struct thread *daemon_ctl_thread = NULL;
+
+static int frr_daemon_ctl(struct thread *t)
+{
+ char buf[1];
+ ssize_t nr;
+
+ nr = recv(daemon_ctl_sock, buf, sizeof(buf), 0);
+ if (nr < 0 && (errno == EINTR || errno == EAGAIN))
+ goto out;
+ if (nr <= 0)
+ return 0;
+
+ switch (buf[0]) {
+ case 'S': /* SIGTSTP */
+ vty_stdio_suspend();
+ send(daemon_ctl_sock, "s", 1, 0);
+ break;
+ case 'R': /* SIGTCNT [implicit] */
+ vty_stdio_resume();
+ break;
+ case 'I': /* SIGINT */
+ di->daemon_mode = false;
+ raise(SIGINT);
+ break;
+ case 'Q': /* SIGQUIT */
+ di->daemon_mode = true;
+ vty_stdio_close();
+ break;
+ }
+
+out:
+ thread_add_read(master, frr_daemon_ctl, NULL, daemon_ctl_sock,
+ &daemon_ctl_thread);
+ return 0;
+}
+
void frr_run(struct thread_master *master)
{
char instanceinfo[64] = "";
@@ -551,6 +805,28 @@ void frr_run(struct thread_master *master)
zlog_notice("%s %s starting: %svty@%d%s", di->name, FRR_VERSION,
instanceinfo, di->vty_port, di->startinfo);
+ if (di->terminal) {
+ vty_stdio(frr_terminal_close);
+ if (daemon_ctl_sock != -1) {
+ set_nonblocking(daemon_ctl_sock);
+ thread_add_read(master, frr_daemon_ctl, NULL,
+ daemon_ctl_sock, &daemon_ctl_thread);
+ }
+ } else {
+ int nullfd = open("/dev/null", O_RDONLY | O_NOCTTY);
+ dup2(nullfd, 0);
+ dup2(nullfd, 1);
+ dup2(nullfd, 2);
+ close(nullfd);
+
+ if (daemon_ctl_sock != -1)
+ close(daemon_ctl_sock);
+ daemon_ctl_sock = -1;
+ }
+
+ /* end fixed stderr startup logging */
+ zlog_startup_stderr = false;
+
struct thread thread;
while (thread_fetch(master, &thread))
thread_call(&thread);
diff --git a/lib/libfrr.h b/lib/libfrr.h
index 23516150ee..1710fc9a84 100644
--- a/lib/libfrr.h
+++ b/lib/libfrr.h
@@ -49,6 +49,7 @@ struct frr_daemon_info {
char *vty_sock_path;
bool dryrun;
bool daemon_mode;
+ bool terminal;
const char *config_file;
const char *pid_file;
const char *vty_path;
diff --git a/lib/libospf.h b/lib/libospf.h
index c9483a4c65..45aedb6a7d 100644
--- a/lib/libospf.h
+++ b/lib/libospf.h
@@ -34,7 +34,7 @@
/* Architectual Constants */
#ifdef DEBUG
-#define OSPF_LS_REFRESH_TIME 60
+#define OSPF_LS_REFRESH_TIME 120
#else
#define OSPF_LS_REFRESH_TIME 1800
#endif
diff --git a/lib/log.c b/lib/log.c
index 5adb06d28c..49351a0656 100644
--- a/lib/log.c
+++ b/lib/log.c
@@ -41,6 +41,7 @@ DEFINE_MTYPE_STATIC(LIB, ZLOG, "Logging")
static int logfile_fd = -1; /* Used in signal handler. */
struct zlog *zlog_default = NULL;
+bool zlog_startup_stderr = true;
const char *zlog_priority[] = {
"emergencies", "alerts", "critical", "errors", "warnings",
@@ -172,6 +173,25 @@ static void time_print(FILE *fp, struct timestamp_control *ctl)
}
+static void vzlog_file(struct zlog *zl, struct timestamp_control *tsctl,
+ const char *proto_str, int record_priority,
+ int priority, FILE *fp, const char *format,
+ va_list args)
+{
+ va_list ac;
+
+ time_print(fp, tsctl);
+ if (record_priority)
+ fprintf(fp, "%s: ", zlog_priority[priority]);
+
+ fprintf(fp, "%s", proto_str);
+ va_copy(ac, args);
+ vfprintf(fp, format, ac);
+ va_end(ac);
+ fprintf(fp, "\n");
+ fflush(fp);
+}
+
/* va_list version of zlog. */
void vzlog(int priority, const char *format, va_list args)
{
@@ -210,32 +230,21 @@ void vzlog(int priority, const char *format, va_list args)
sprintf(proto_str, "%s: ", zl->protoname);
/* File output. */
- if ((priority <= zl->maxlvl[ZLOG_DEST_FILE]) && zl->fp) {
- va_list ac;
- time_print(zl->fp, &tsctl);
- if (zl->record_priority)
- fprintf(zl->fp, "%s: ", zlog_priority[priority]);
- fprintf(zl->fp, "%s", proto_str);
- va_copy(ac, args);
- vfprintf(zl->fp, format, ac);
- va_end(ac);
- fprintf(zl->fp, "\n");
- fflush(zl->fp);
- }
-
- /* stdout output. */
- if (priority <= zl->maxlvl[ZLOG_DEST_STDOUT]) {
- va_list ac;
- time_print(stdout, &tsctl);
- if (zl->record_priority)
- fprintf(stdout, "%s: ", zlog_priority[priority]);
- fprintf(stdout, "%s", proto_str);
- va_copy(ac, args);
- vfprintf(stdout, format, ac);
- va_end(ac);
- fprintf(stdout, "\n");
- fflush(stdout);
- }
+ if ((priority <= zl->maxlvl[ZLOG_DEST_FILE]) && zl->fp)
+ vzlog_file(zl, &tsctl, proto_str, zl->record_priority,
+ priority, zl->fp, format, args);
+
+ /* fixed-config logging to stderr while we're stating up & haven't
+ * daemonized / reached mainloop yet
+ *
+ * note the "else" on stdout output -- we don't want to print the same
+ * message to both stderr and stdout. */
+ if (zlog_startup_stderr && priority <= LOG_WARNING)
+ vzlog_file(zl, &tsctl, proto_str, 1,
+ priority, stderr, format, args);
+ else if (priority <= zl->maxlvl[ZLOG_DEST_STDOUT])
+ vzlog_file(zl, &tsctl, proto_str, zl->record_priority,
+ priority, stdout, format, args);
/* Terminal monitor. */
if (priority <= zl->maxlvl[ZLOG_DEST_MONITOR])
@@ -916,6 +925,11 @@ static const struct zebra_desc_table command_types[] = {
DESC_ENTRY(ZEBRA_MACIP_DEL),
DESC_ENTRY(ZEBRA_REMOTE_MACIP_ADD),
DESC_ENTRY(ZEBRA_REMOTE_MACIP_DEL),
+ DESC_ENTRY(ZEBRA_PW_ADD),
+ DESC_ENTRY(ZEBRA_PW_DELETE),
+ DESC_ENTRY(ZEBRA_PW_SET),
+ DESC_ENTRY(ZEBRA_PW_UNSET),
+ DESC_ENTRY(ZEBRA_PW_STATUS_UPDATE),
};
#undef DESC_ENTRY
diff --git a/lib/log.h b/lib/log.h
index d872ce56d6..07eb6d5bd5 100644
--- a/lib/log.h
+++ b/lib/log.h
@@ -24,6 +24,7 @@
#include <syslog.h>
#include <stdint.h>
+#include <stdbool.h>
#include <stdio.h>
/* Here is some guidance on logging levels to use:
@@ -54,6 +55,8 @@ typedef enum {
} zlog_dest_t;
#define ZLOG_NUM_DESTS (ZLOG_DEST_FILE+1)
+extern bool zlog_startup_stderr;
+
/* Message structure. */
struct message {
int key;
diff --git a/lib/monotime.h b/lib/monotime.h
index 7bd3386498..8e50c1874a 100644
--- a/lib/monotime.h
+++ b/lib/monotime.h
@@ -49,6 +49,10 @@ static inline time_t monotime(struct timeval *tvo)
return ts.tv_sec;
}
+#define ONE_DAY_SECOND 60*60*24
+#define ONE_WEEK_SECOND ONE_DAY_SECOND*7
+#define ONE_YEAR_SECOND ONE_DAY_SECOND*365
+
/* the following two return microseconds, not time_t!
*
* also, they're negative forms of each other, but having both makes the
diff --git a/lib/privs.c b/lib/privs.c
index c971596117..eda3fb02d4 100644
--- a/lib/privs.c
+++ b/lib/privs.c
@@ -696,13 +696,10 @@ static int getgrouplist(const char *user, gid_t group, gid_t *groups,
}
#endif /* HAVE_GETGROUPLIST */
-void zprivs_init(struct zebra_privs_t *zprivs)
+void zprivs_preinit(struct zebra_privs_t *zprivs)
{
struct passwd *pwentry = NULL;
struct group *grentry = NULL;
- gid_t groups[NGROUPS_MAX];
- int i, ngroups = 0;
- int found = 0;
if (!zprivs) {
fprintf(stderr, "zprivs_init: called with NULL arg!\n");
@@ -751,6 +748,18 @@ void zprivs_init(struct zebra_privs_t *zprivs)
zprivs_state.zgid = grentry->gr_gid;
}
+}
+
+void zprivs_init(struct zebra_privs_t *zprivs)
+{
+ gid_t groups[NGROUPS_MAX];
+ int i, ngroups = 0;
+ int found = 0;
+
+ /* NULL privs */
+ if (!(zprivs->user || zprivs->group || zprivs->cap_num_p
+ || zprivs->cap_num_i))
+ return;
if (zprivs->user) {
ngroups = sizeof(groups);
diff --git a/lib/privs.h b/lib/privs.h
index c18fe78add..7fe59328b2 100644
--- a/lib/privs.h
+++ b/lib/privs.h
@@ -74,6 +74,7 @@ struct zprivs_ids_t {
};
/* initialise zebra privileges */
+extern void zprivs_preinit(struct zebra_privs_t *zprivs);
extern void zprivs_init(struct zebra_privs_t *zprivs);
/* drop all and terminate privileges */
extern void zprivs_terminate(struct zebra_privs_t *);
diff --git a/lib/pw.h b/lib/pw.h
new file mode 100644
index 0000000000..2cfaa47e5d
--- /dev/null
+++ b/lib/pw.h
@@ -0,0 +1,52 @@
+/* Pseudowire definitions
+ * Copyright (C) 2016 Volta Networks, Inc.
+ *
+ * This program 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 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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.
+ *
+ * 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
+ */
+
+#ifndef _FRR_PW_H
+#define _FRR_PW_H
+
+/* L2VPN name length. */
+#define L2VPN_NAME_LEN 32
+
+/* Pseudowire type - LDP and BGP use the same values. */
+#define PW_TYPE_ETHERNET_TAGGED 0x0004 /* RFC 4446 */
+#define PW_TYPE_ETHERNET 0x0005 /* RFC 4446 */
+#define PW_TYPE_WILDCARD 0x7FFF /* RFC 4863, RFC 6668 */
+
+/* Pseudowire flags. */
+#define F_PSEUDOWIRE_CWORD 0x01
+
+/* Pseudowire status. */
+#define PW_STATUS_DOWN 0
+#define PW_STATUS_UP 1
+
+/*
+ * Protocol-specific information about the pseudowire.
+ */
+union pw_protocol_fields {
+ struct {
+ struct in_addr lsr_id;
+ uint32_t pwid;
+ char vpn_name[L2VPN_NAME_LEN];
+ } ldp;
+ struct {
+ /* TODO */
+ } bgp;
+};
+
+#endif /* _FRR_PW_H */
diff --git a/lib/subdir.am b/lib/subdir.am
index 28a4ce5579..6a62cbb678 100644
--- a/lib/subdir.am
+++ b/lib/subdir.am
@@ -123,8 +123,8 @@ pkginclude_HEADERS += \
lib/prefix.h \
lib/privs.h \
lib/ptm_lib.h \
+ lib/pw.h \
lib/qobj.h \
- lib/route_types.h \
lib/routemap.h \
lib/sbuf.h \
lib/sha256.h \
@@ -141,7 +141,6 @@ pkginclude_HEADERS += \
lib/termtable.h \
lib/thread.h \
lib/vector.h \
- lib/version.h \
lib/vlan.h \
lib/vrf.h \
lib/vrf_int.h \
@@ -154,6 +153,11 @@ pkginclude_HEADERS += \
lib/zebra.h \
# end
+nodist_pkginclude_HEADERS += \
+ lib/route_types.h \
+ lib/version.h \
+ # end
+
noinst_HEADERS += \
lib/clippy.h \
lib/log_int.h \
@@ -180,16 +184,22 @@ lib_libfrrsnmp_la_SOURCES = \
# CLI utilities
#
noinst_PROGRAMS += \
- lib/clippy \
lib/grammar_sandbox \
# end
+if BUILD_CLIPPY
+noinst_PROGRAMS += lib/clippy
+else
+$(HOSTTOOLS)lib/clippy:
+ @$(MAKE) -C $(top_builddir)/$(HOSTTOOLS) lib/route_types.h lib/clippy
+endif
+
lib_grammar_sandbox_SOURCES = \
lib/grammar_sandbox_main.c
lib_grammar_sandbox_LDADD = \
lib/libfrr.la
-lib_clippy_CPPFLAGS = -D_GNU_SOURCE -I$(top_srcdir)/lib
+lib_clippy_CPPFLAGS = $(AM_CPPFLAGS) -D_GNU_SOURCE
lib_clippy_CFLAGS = $(PYTHON_CFLAGS)
lib_clippy_LDADD = $(PYTHON_LIBS)
lib_clippy_SOURCES = \
@@ -210,6 +220,7 @@ lib_clippy_SOURCES = \
#
EXTRA_DIST += \
lib/command_lex.h \
+ lib/command_parse.h \
lib/gitversion.pl \
lib/queue.h \
lib/route_types.pl \
@@ -217,8 +228,6 @@ EXTRA_DIST += \
# end
BUILT_SOURCES += \
- lib/command_lex.h \
- lib/command_parse.h \
lib/gitversion.h \
lib/route_types.h \
# end
diff --git a/lib/thread.c b/lib/thread.c
index fd36eaf388..4a5c61d036 100644
--- a/lib/thread.c
+++ b/lib/thread.c
@@ -340,9 +340,6 @@ static void cancelreq_del(void *cr)
/* initializer, only ever called once */
static void initializer()
{
- if (!masters)
- masters = list_new();
-
pthread_key_create(&thread_current, NULL);
}
@@ -415,9 +412,12 @@ struct thread_master *thread_master_create(const char *name)
rv->handler.copy = XCALLOC(MTYPE_THREAD_MASTER,
sizeof(struct pollfd) * rv->handler.pfdsize);
- /* add to list */
+ /* add to list of threadmasters */
pthread_mutex_lock(&masters_mtx);
{
+ if (!masters)
+ masters = list_new();
+
listnode_add(masters, rv);
}
pthread_mutex_unlock(&masters_mtx);
@@ -551,6 +551,10 @@ void thread_master_free(struct thread_master *m)
pthread_mutex_lock(&masters_mtx);
{
listnode_delete(masters, m);
+ if (masters->count == 0) {
+ list_free (masters);
+ masters = NULL;
+ }
}
pthread_mutex_unlock(&masters_mtx);
diff --git a/lib/vty.c b/lib/vty.c
index e5bf2e6ced..59a8825357 100644
--- a/lib/vty.c
+++ b/lib/vty.c
@@ -684,6 +684,7 @@ static void vty_end_config(struct vty *vty)
break;
case CONFIG_NODE:
case INTERFACE_NODE:
+ case PW_NODE:
case ZEBRA_NODE:
case RIP_NODE:
case RIPNG_NODE:
@@ -1094,6 +1095,7 @@ static void vty_stop_input(struct vty *vty)
break;
case CONFIG_NODE:
case INTERFACE_NODE:
+ case PW_NODE:
case ZEBRA_NODE:
case RIP_NODE:
case RIPNG_NODE:
@@ -1661,24 +1663,82 @@ static struct vty *vty_create(int vty_sock, union sockunion *su)
/* create vty for stdio */
static struct termios stdio_orig_termios;
static struct vty *stdio_vty = NULL;
-static void (*stdio_vty_atclose)(void);
+static bool stdio_termios = false;
+static void (*stdio_vty_atclose)(int isexit);
-static void vty_stdio_reset(void)
+static void vty_stdio_reset(int isexit)
{
if (stdio_vty) {
- tcsetattr(0, TCSANOW, &stdio_orig_termios);
+ if (stdio_termios)
+ tcsetattr(0, TCSANOW, &stdio_orig_termios);
+ stdio_termios = false;
+
stdio_vty = NULL;
if (stdio_vty_atclose)
- stdio_vty_atclose();
+ stdio_vty_atclose(isexit);
stdio_vty_atclose = NULL;
}
}
-struct vty *vty_stdio(void (*atclose)())
+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))
{
struct vty *vty;
- struct termios termios;
/* refuse creating two vtys on stdio */
if (stdio_vty)
@@ -1696,23 +1756,7 @@ struct vty *vty_stdio(void (*atclose)())
vty->v_timeout = 0;
strcpy(vty->address, "console");
- if (!tcgetattr(0, &stdio_orig_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 | ISIG | IEXTEN);
- termios.c_cflag &= ~(CSIZE | PARENB);
- termios.c_cflag |= CS8;
- tcsetattr(0, TCSANOW, &termios);
- }
-
- vty_prompt(vty);
-
- /* Add read/write thread. */
- vty_event(VTY_WRITE, 1, vty);
- vty_event(VTY_READ, 0, vty);
-
+ vty_stdio_resume();
return vty;
}
@@ -1811,7 +1855,7 @@ static void vty_serv_sock_addrinfo(const char *hostname, unsigned short port)
ret = getaddrinfo(hostname, port_str, &req, &ainfo);
if (ret != 0) {
- fprintf(stderr, "getaddrinfo failed: %s\n", gai_strerror(ret));
+ zlog_err("getaddrinfo failed: %s", gai_strerror(ret));
exit(1);
}
@@ -2161,7 +2205,7 @@ void vty_close(struct vty *vty)
XFREE(MTYPE_VTY, vty);
if (was_stdio)
- vty_stdio_reset();
+ vty_stdio_reset(0);
}
/* When time out occur output message then close connection. */
@@ -2211,23 +2255,22 @@ static void vty_read_file(FILE *confp)
if (!((ret == CMD_SUCCESS) || (ret == CMD_ERR_NOTHING_TODO))) {
const char *message = NULL;
+ char *nl;
+
switch (ret) {
case CMD_ERR_AMBIGUOUS:
- message =
- "*** Error reading config: Ambiguous command.";
+ message = "Ambiguous command";
break;
case CMD_ERR_NO_MATCH:
- message =
- "*** Error reading config: There is no such command.";
+ message = "No such command";
break;
}
- fprintf(stderr, "%s\n", message);
- zlog_err("%s", message);
- fprintf(stderr,
- "*** Error occurred processing line %u, below:\n%s\n",
- line_num, vty->error_buf);
- zlog_err("*** Error occurred processing line %u, below:\n%s",
- line_num, vty->error_buf);
+
+ nl = strchr(vty->error_buf, '\n');
+ if (nl)
+ *nl = '\0';
+ zlog_err("ERROR: %s on config line %u: %s",
+ message, line_num, vty->error_buf);
}
vty_close(vty);
@@ -2299,8 +2342,7 @@ void vty_read_config(const char *config_file, char *config_default_dir)
if (config_file != NULL) {
if (!IS_DIRECTORY_SEP(config_file[0])) {
if (getcwd(cwd, MAXPATHLEN) == NULL) {
- fprintf(stderr,
- "Failure to determine Current Working Directory %d!\n",
+ zlog_err("Failure to determine Current Working Directory %d!",
errno);
exit(1);
}
@@ -2314,17 +2356,14 @@ void vty_read_config(const char *config_file, char *config_default_dir)
confp = fopen(fullpath, "r");
if (confp == NULL) {
- fprintf(stderr,
- "%s: failed to open configuration file %s: %s\n",
+ zlog_err("%s: failed to open configuration file %s: %s",
__func__, fullpath, safe_strerror(errno));
confp = vty_use_backup_config(fullpath);
if (confp)
- fprintf(stderr,
- "WARNING: using backup configuration file!\n");
+ zlog_warn("WARNING: using backup configuration file!");
else {
- fprintf(stderr,
- "can't open configuration file [%s]\n",
+ zlog_err("can't open configuration file [%s]",
config_file);
exit(1);
}
@@ -2359,19 +2398,16 @@ void vty_read_config(const char *config_file, char *config_default_dir)
#endif /* VTYSH */
confp = fopen(config_default_dir, "r");
if (confp == NULL) {
- fprintf(stderr,
- "%s: failed to open configuration file %s: %s\n",
+ zlog_err("%s: failed to open configuration file %s: %s",
__func__, config_default_dir,
safe_strerror(errno));
confp = vty_use_backup_config(config_default_dir);
if (confp) {
- fprintf(stderr,
- "WARNING: using backup configuration file!\n");
+ zlog_warn("WARNING: using backup configuration file!");
fullpath = config_default_dir;
} else {
- fprintf(stderr,
- "can't open configuration file [%s]\n",
+ zlog_err("can't open configuration file [%s]",
config_default_dir);
goto tmp_free_and_out;
}
@@ -2881,12 +2917,12 @@ static void vty_save_cwd(void)
* Hence not worrying about it too much.
*/
if (!chdir(SYSCONFDIR)) {
- fprintf(stderr, "Failure to chdir to %s, errno: %d\n",
+ zlog_err("Failure to chdir to %s, errno: %d",
SYSCONFDIR, errno);
exit(-1);
}
if (getcwd(cwd, MAXPATHLEN) == NULL) {
- fprintf(stderr, "Failure to getcwd, errno: %d\n",
+ zlog_err("Failure to getcwd, errno: %d",
errno);
exit(-1);
}
@@ -2926,7 +2962,7 @@ void vty_init(struct thread_master *master_thread)
vty_master = master_thread;
- atexit(vty_stdio_reset);
+ atexit(vty_stdio_atexit);
/* Initilize server thread vector. */
Vvty_serv_thread = vector_init(VECTOR_MIN_SIZE);
diff --git a/lib/vty.h b/lib/vty.h
index 0980f73afa..dcb8da225d 100644
--- a/lib/vty.h
+++ b/lib/vty.h
@@ -257,7 +257,7 @@ extern void vty_init_vtysh(void);
extern void vty_terminate(void);
extern void vty_reset(void);
extern struct vty *vty_new(void);
-extern struct vty *vty_stdio(void (*atclose)(void));
+extern struct vty *vty_stdio(void (*atclose)(int isexit));
extern int vty_out(struct vty *, const char *, ...) PRINTF_ATTRIBUTE(2, 3);
extern void vty_read_config(const char *, char *);
extern void vty_time_print(struct vty *, int);
@@ -273,6 +273,11 @@ extern int vty_shell(struct vty *);
extern int vty_shell_serv(struct vty *);
extern void vty_hello(struct vty *);
+/* ^Z / SIGTSTP handling */
+extern void vty_stdio_suspend(void);
+extern void vty_stdio_resume(void);
+extern void vty_stdio_close(void);
+
/* Send a fixed-size message to all vty terminal monitors; this should be
an async-signal-safe function. */
extern void vty_log_fixed(char *buf, size_t len);
diff --git a/lib/zclient.c b/lib/zclient.c
index 233d0359d5..24cb699196 100644
--- a/lib/zclient.c
+++ b/lib/zclient.c
@@ -1778,6 +1778,69 @@ int lm_release_label_chunk(struct zclient *zclient, uint32_t start,
return 0;
}
+int zebra_send_pw(struct zclient *zclient, int command, struct zapi_pw *pw)
+{
+ struct stream *s;
+
+ /* Reset stream. */
+ s = zclient->obuf;
+ stream_reset(s);
+
+ zclient_create_header(s, command, VRF_DEFAULT);
+ stream_write(s, pw->ifname, IF_NAMESIZE);
+ stream_putl(s, pw->ifindex);
+
+ /* Put type */
+ stream_putl(s, pw->type);
+
+ /* Put nexthop */
+ stream_putl(s, pw->af);
+ switch (pw->af) {
+ case AF_INET:
+ stream_put_in_addr(s, &pw->nexthop.ipv4);
+ break;
+ case AF_INET6:
+ stream_write(s, (u_char *)&pw->nexthop.ipv6, 16);
+ break;
+ default:
+ zlog_err("%s: unknown af", __func__);
+ return -1;
+ }
+
+ /* Put labels */
+ stream_putl(s, pw->local_label);
+ stream_putl(s, pw->remote_label);
+
+ /* Put flags */
+ stream_putc(s, pw->flags);
+
+ /* Protocol specific fields */
+ stream_write(s, &pw->data, sizeof(union pw_protocol_fields));
+
+ /* Put length at the first point of the stream. */
+ stream_putw_at(s, 0, stream_get_endp(s));
+
+ return zclient_send_message(zclient);
+}
+
+/*
+ * Receive PW status update from Zebra and send it to LDE process.
+ */
+void zebra_read_pw_status_update(int command, struct zclient *zclient,
+ zebra_size_t length, vrf_id_t vrf_id,
+ struct zapi_pw_status *pw)
+{
+ struct stream *s;
+
+ memset(pw, 0, sizeof(struct zapi_pw_status));
+ s = zclient->ibuf;
+
+ /* Get data. */
+ stream_get(pw->ifname, s, IF_NAMESIZE);
+ pw->ifindex = stream_getl(s);
+ pw->status = stream_getl(s);
+}
+
/* Zebra client message read function. */
static int zclient_read(struct thread *thread)
{
@@ -2005,6 +2068,11 @@ static int zclient_read(struct thread *thread)
(*zclient->local_macip_del)(command, zclient, length,
vrf_id);
break;
+ case ZEBRA_PW_STATUS_UPDATE:
+ if (zclient->pw_status_update)
+ (*zclient->pw_status_update)(command, zclient, length,
+ vrf_id);
+ break;
default:
break;
}
diff --git a/lib/zclient.h b/lib/zclient.h
index 2752acc281..5edb56f517 100644
--- a/lib/zclient.h
+++ b/lib/zclient.h
@@ -30,6 +30,12 @@
/* For vrf_bitmap_t. */
#include "vrf.h"
+/* For union g_addr */
+#include "nexthop.h"
+
+/* For union pw_protocol_fields */
+#include "pw.h"
+
/* For input/output buffer to zebra. */
#define ZEBRA_MAX_PACKET_SIZ 4096
@@ -114,6 +120,11 @@ typedef enum {
ZEBRA_MACIP_DEL,
ZEBRA_REMOTE_MACIP_ADD,
ZEBRA_REMOTE_MACIP_DEL,
+ ZEBRA_PW_ADD,
+ ZEBRA_PW_DELETE,
+ ZEBRA_PW_SET,
+ ZEBRA_PW_UNSET,
+ ZEBRA_PW_STATUS_UPDATE,
} zebra_message_types_t;
struct redist_proto {
@@ -195,6 +206,7 @@ struct zclient {
int (*local_vni_del)(int, struct zclient *, uint16_t, vrf_id_t);
int (*local_macip_add)(int, struct zclient *, uint16_t, vrf_id_t);
int (*local_macip_del)(int, struct zclient *, uint16_t, vrf_id_t);
+ int (*pw_status_update)(int, struct zclient *, uint16_t, vrf_id_t);
};
/* Zebra API message flag. */
@@ -274,6 +286,25 @@ struct zapi_ipv4 {
vrf_id_t vrf_id;
};
+struct zapi_pw {
+ char ifname[IF_NAMESIZE];
+ ifindex_t ifindex;
+ int type;
+ int af;
+ union g_addr nexthop;
+ uint32_t local_label;
+ uint32_t remote_label;
+ uint8_t flags;
+ union pw_protocol_fields data;
+ uint8_t protocol;
+};
+
+struct zapi_pw_status {
+ char ifname[IF_NAMESIZE];
+ ifindex_t ifindex;
+ uint32_t status;
+};
+
/* Prototypes of zebra client service functions. */
extern struct zclient *zclient_new(struct thread_master *);
extern void zclient_init(struct zclient *, int, u_short);
@@ -344,6 +375,12 @@ extern int lm_get_label_chunk(struct zclient *zclient, u_char keep,
uint32_t *end);
extern int lm_release_label_chunk(struct zclient *zclient, uint32_t start,
uint32_t end);
+extern int zebra_send_pw(struct zclient *zclient, int command,
+ struct zapi_pw *pw);
+extern void zebra_read_pw_status_update(int command, struct zclient *zclient,
+ zebra_size_t length, vrf_id_t vrf_id,
+ struct zapi_pw_status *pw);
+
/* IPv6 prefix add and delete function prototype. */
struct zapi_ipv6 {