summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore2
-rw-r--r--Makefile.am5
-rw-r--r--SERVICES2
-rw-r--r--bgpd/bgp_main.c227
-rw-r--r--bgpd/bgp_updgrp.h1
-rw-r--r--bgpd/bgpd.c4
-rw-r--r--bgpd/bgpd.h2
-rw-r--r--bgpd/rfapi/rfapi_vty.c5
-rwxr-xr-xconfigure.ac46
-rw-r--r--cumulus/etc/frr/daemons1
-rw-r--r--cumulus/etc/frr/debian.conf1
-rw-r--r--doc/Makefile.am9
-rw-r--r--doc/bgpd.8.in1
-rw-r--r--doc/frr.texi2
-rw-r--r--doc/install.texi1
-rw-r--r--doc/nhrpd.8.in105
-rw-r--r--doc/nhrpd.texi143
-rw-r--r--doc/zebra.8.in1
-rw-r--r--isisd/isis_main.c188
-rw-r--r--ldpd/ldpd.c175
-rw-r--r--ldpd/log.c3
-rw-r--r--lib/Makefile.am8
-rw-r--r--lib/command.c28
-rw-r--r--lib/filter.c4
-rw-r--r--lib/grammar_sandbox_main.c8
-rw-r--r--lib/if.c4
-rw-r--r--lib/libfrr.c392
-rw-r--r--lib/libfrr.h100
-rw-r--r--lib/log.c113
-rw-r--r--lib/log.h71
-rw-r--r--lib/log_int.h57
-rw-r--r--lib/plist.c3
-rw-r--r--lib/prefix.c3
-rw-r--r--lib/route_types.txt2
-rw-r--r--lib/routemap.c11
-rw-r--r--lib/sockopt.c23
-rw-r--r--lib/sockopt.h4
-rw-r--r--lib/sockunion.c37
-rw-r--r--lib/thread.c3
-rw-r--r--lib/thread.h5
-rw-r--r--lib/vty.c33
-rw-r--r--lib/vty.h2
-rw-r--r--nhrpd/Makefile.am47
-rw-r--r--nhrpd/README.kernel145
-rw-r--r--nhrpd/README.nhrpd137
-rw-r--r--nhrpd/debug.h42
-rw-r--r--nhrpd/linux.c153
-rw-r--r--nhrpd/list.h191
-rw-r--r--nhrpd/netlink.h24
-rw-r--r--nhrpd/netlink_arp.c275
-rw-r--r--nhrpd/netlink_gre.c142
-rw-r--r--nhrpd/nhrp_cache.c343
-rw-r--r--nhrpd/nhrp_event.c280
-rw-r--r--nhrpd/nhrp_interface.c406
-rw-r--r--nhrpd/nhrp_main.c154
-rw-r--r--nhrpd/nhrp_nhs.c372
-rw-r--r--nhrpd/nhrp_packet.c312
-rw-r--r--nhrpd/nhrp_peer.c862
-rw-r--r--nhrpd/nhrp_protocol.h128
-rw-r--r--nhrpd/nhrp_route.c347
-rw-r--r--nhrpd/nhrp_shortcut.c404
-rw-r--r--nhrpd/nhrp_vc.c219
-rw-r--r--nhrpd/nhrp_vty.c924
-rw-r--r--nhrpd/nhrpd.h403
-rw-r--r--nhrpd/os.h5
-rw-r--r--nhrpd/reqid.c49
-rw-r--r--nhrpd/resolver.c190
-rw-r--r--nhrpd/vici.c482
-rw-r--r--nhrpd/vici.h24
-rw-r--r--nhrpd/zbuf.c220
-rw-r--r--nhrpd/zbuf.h189
-rw-r--r--nhrpd/znl.c160
-rw-r--r--nhrpd/znl.h29
-rw-r--r--ospf6d/ospf6_main.c192
-rw-r--r--ospfd/ospf_ism.c20
-rw-r--r--ospfd/ospf_main.c211
-rw-r--r--ospfd/ospf_nsm.c8
-rw-r--r--ospfd/ospf_packet.c13
-rw-r--r--ospfd/ospf_snmp.c14
-rw-r--r--ospfd/ospf_vty.c2
-rw-r--r--ospfd/ospfd.c4
-rw-r--r--ospfd/ospfd.h2
-rw-r--r--pimd/pim_main.c151
-rw-r--r--pimd/pim_signals.c10
-rw-r--r--pimd/pim_signals.h3
-rw-r--r--pimd/pim_zebra.c5
-rw-r--r--pimd/pim_zebra.h2
-rw-r--r--pimd/pimd.c2
-rw-r--r--redhat/frr.spec.in32
-rw-r--r--ripd/rip_interface.c5
-rw-r--r--ripd/rip_main.c187
-rw-r--r--ripd/ripd.c3
-rw-r--r--ripngd/ripng_main.c183
-rw-r--r--ripngd/ripngd.c10
-rw-r--r--tests/bgpd/test_aspath.c2
-rw-r--r--tests/bgpd/test_capability.c2
-rw-r--r--tests/bgpd/test_mp_attr.c2
-rw-r--r--tests/bgpd/test_mpath.c2
-rw-r--r--tests/lib/cli/common_cli.c12
-rw-r--r--tests/lib/test_segv.c9
-rw-r--r--tests/lib/test_sig.c13
-rwxr-xr-xtools/frr2
-rw-r--r--vtysh/Makefile.am4
-rw-r--r--vtysh/vtysh.c70
-rw-r--r--vtysh/vtysh.h7
-rw-r--r--vtysh/vtysh_main.c6
-rw-r--r--watchfrr/watchfrr.c158
-rw-r--r--zebra/client_main.c1
-rw-r--r--zebra/if_ioctl_solaris.c12
-rw-r--r--zebra/if_sysctl.c6
-rw-r--r--zebra/ioctl.c14
-rw-r--r--zebra/ioctl_solaris.c12
-rw-r--r--zebra/ipforward_sysctl.c30
-rw-r--r--zebra/kernel_netlink.c68
-rw-r--r--zebra/kernel_socket.c7
-rw-r--r--zebra/main.c213
-rw-r--r--zebra/rt_socket.c6
-rw-r--r--zebra/test_main.c7
-rw-r--r--zebra/zebra_rib.c5
-rw-r--r--zebra/zebra_rnh.c25
-rw-r--r--zebra/zebra_vty.c3
-rw-r--r--zebra/zserv.c4
122 files changed, 9185 insertions, 2069 deletions
diff --git a/.gitignore b/.gitignore
index 6592b34004..6f281112b0 100644
--- a/.gitignore
+++ b/.gitignore
@@ -30,6 +30,7 @@ Makefile.in
*.tar.gz.asc
.nfs*
libtool
+.libs
.arch-inventory
.arch-ids
{arch}
@@ -37,6 +38,7 @@ build
.msg
.rebase-*
*~
+*.o
*.loT
m4/*.m4
!m4/ax_sys_weak_alias.m4
diff --git a/Makefile.am b/Makefile.am
index 63dedeac06..aa978b7d25 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -2,12 +2,13 @@
SUBDIRS = lib qpb fpm @ZEBRA@ @LIBRFP@ @RFPTEST@ \
@BGPD@ @RIPD@ @RIPNGD@ @OSPFD@ @OSPF6D@ @LDPD@ \
- @ISISD@ @PIMD@ @WATCHFRR@ @VTYSH@ @OSPFCLIENT@ @DOC@ m4 @pkgsrcdir@ \
+ @ISISD@ @PIMD@ @NHRPD@ \
+ @WATCHFRR@ @VTYSH@ @OSPFCLIENT@ @DOC@ m4 @pkgsrcdir@ \
redhat @SOLARIS@ tests tools cumulus snapcraft
DIST_SUBDIRS = lib qpb fpm zebra bgpd ripd ripngd ospfd ospf6d ldpd \
isisd watchfrr vtysh ospfclient doc m4 pkgsrc redhat tests \
- solaris pimd @LIBRFP@ @RFPTEST@ tools cumulus snapcraft
+ solaris pimd nhrpd @LIBRFP@ @RFPTEST@ tools cumulus snapcraft
EXTRA_DIST = aclocal.m4 SERVICES REPORTING-BUGS \
update-autotools \
diff --git a/SERVICES b/SERVICES
index ee242ca6a2..50c79391d7 100644
--- a/SERVICES
+++ b/SERVICES
@@ -17,5 +17,7 @@ bgpd 2605/tcp
ospf6d 2606/tcp
ospfapi 2607/tcp
isisd 2608/tcp
+# babeld 2609/tcp
+nhrpd 2610/tcp
pimd 2611/tcp
ldpd 2612/tcp
diff --git a/bgpd/bgp_main.c b/bgpd/bgp_main.c
index 2598d66656..423c9453eb 100644
--- a/bgpd/bgp_main.c
+++ b/bgpd/bgp_main.c
@@ -39,7 +39,7 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
#include "queue.h"
#include "vrf.h"
#include "bfd.h"
-#include "sockopt.h"
+#include "libfrr.h"
#include "bgpd/bgpd.h"
#include "bgpd/bgp_attr.h"
@@ -60,27 +60,13 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
#endif
/* bgpd options, we use GNU getopt library. */
-#define OPTION_VTYSOCK 1000
static const struct option longopts[] =
{
- { "daemon", no_argument, NULL, 'd'},
- { "config_file", required_argument, NULL, 'f'},
- { "pid_file", required_argument, NULL, 'i'},
- { "socket", required_argument, NULL, 'z'},
{ "bgp_port", required_argument, NULL, 'p'},
{ "listenon", required_argument, NULL, 'l'},
- { "vty_addr", required_argument, NULL, 'A'},
- { "vty_port", required_argument, NULL, 'P'},
- { "vty_socket", required_argument, NULL, OPTION_VTYSOCK },
{ "retain", no_argument, NULL, 'r'},
{ "no_kernel", no_argument, NULL, 'n'},
{ "ecmp", required_argument, NULL, 'e'},
- { "user", required_argument, NULL, 'u'},
- { "group", required_argument, NULL, 'g'},
- { "skip_runas", no_argument, NULL, 'S'},
- { "version", no_argument, NULL, 'v'},
- { "dryrun", no_argument, NULL, 'C'},
- { "help", no_argument, NULL, 'h'},
{ 0 }
};
@@ -112,26 +98,9 @@ static struct quagga_signal_t bgp_signals[] =
},
};
-/* Configuration file and directory. */
-char config_default[] = SYSCONFDIR BGP_DEFAULT_CONFIG;
-
-/* VTY Socket prefix */
-char vty_sock_path[MAXPATHLEN] = BGP_VTYSH_PATH;
-
/* Route retain mode flag. */
static int retain_mode = 0;
-/* Manually specified configuration file name. */
-char *config_file = NULL;
-
-/* Process ID saved for use by init system */
-static const char *pid_file = PATH_BGPD_PID;
-
-/* VTY port number and address. */
-int vty_port = BGP_VTY_PORT;
-char *vty_addr = NULL;
-char *vty_sock_name;
-
/* privileges */
static zebra_capabilities_t _caps_p [] =
{
@@ -154,41 +123,7 @@ struct zebra_privs_t bgpd_privs =
.cap_num_i = 0,
};
-/* Help information display. */
-static void
-usage (char *progname, int status)
-{
- if (status != 0)
- fprintf (stderr, "Try `%s --help' for more information.\n", progname);
- else
- {
- printf ("Usage : %s [OPTION...]\n\n\
-Daemon which manages kernel routing table management and \
-redistribution between different routing protocols.\n\n\
--d, --daemon Runs in daemon mode\n\
--f, --config_file Set configuration file name\n\
--i, --pid_file Set process identifier file name\n\
--z, --socket Set path of zebra socket\n\
--p, --bgp_port Set bgp protocol's port number\n\
--l, --listenon Listen on specified address (implies -n)\n\
--A, --vty_addr Set vty's bind address\n\
--P, --vty_port Set vty's port number\n\
- --vty_socket Override vty socket path\n\
--r, --retain When program terminates, retain added route by bgpd.\n\
--n, --no_kernel Do not install route to kernel.\n\
--e, --ecmp Specify ECMP to use.\n\
--u, --user User to run as\n\
--g, --group Group to run as\n\
--S, --skip_runas Skip user and group run as\n\
--v, --version Print program version\n\
--C, --dryrun Check configuration for validity and exit\n\
--h, --help Display this help and exit\n\
-\n\
-Report bugs to %s\n", progname, FRR_BUG_ADDRESS);
- }
-
- exit (status);
-}
+static struct frr_daemon_info bgpd_di;
/* SIGHUP handler. */
void
@@ -202,10 +137,7 @@ sighup (void)
zlog_info ("bgpd restarting!");
/* Reload config file. */
- vty_read_config (config_file, config_default);
-
- /* Create VTY's socket */
- vty_serv_sock (vty_addr, vty_port, vty_sock_path);
+ vty_read_config (bgpd_di.config_file, config_default);
/* Try to return to normal operation. */
}
@@ -219,8 +151,7 @@ sigint (void)
if (! retain_mode)
{
bgp_terminate ();
- if (bgpd_privs.user) /* NULL if skip_runas flag set */
- zprivs_terminate (&bgpd_privs);
+ zprivs_terminate (&bgpd_privs);
}
bgp_exit (0);
@@ -232,7 +163,7 @@ sigint (void)
void
sigusr1 (void)
{
- zlog_rotate (NULL);
+ zlog_rotate();
}
/*
@@ -309,8 +240,7 @@ bgp_exit (int status)
if (bm->master)
thread_master_free (bm->master);
- if (zlog_default)
- closezlog (zlog_default);
+ closezlog ();
memset (bm, 0, sizeof (*bm));
@@ -412,33 +342,40 @@ bgp_vrf_terminate (void)
vrf_terminate ();
}
+FRR_DAEMON_INFO(bgpd, BGP,
+ .vty_port = BGP_VTY_PORT,
+
+ .proghelp = "Implementation of the BGP routing protocol.",
+
+ .signals = bgp_signals,
+ .n_signals = array_size(bgp_signals),
+
+ .privs = &bgpd_privs,
+)
+
/* Main routine of bgpd. Treatment of argument and start bgp finite
state machine is handled at here. */
int
main (int argc, char **argv)
{
- char *p;
int opt;
- int daemon_mode = 0;
- int dryrun = 0;
- char *progname;
- struct thread thread;
int tmp_port;
- int skip_runas = 0;
- /* Set umask before anything for security */
- umask (0027);
+ int bgp_port = BGP_PORT_DEFAULT;
+ char *bgp_address = NULL;
- /* Preserve name of myself. */
- progname = ((p = strrchr (argv[0], '/')) ? ++p : argv[0]);
-
- /* BGP master init. */
- bgp_master_init ();
+ frr_preinit(&bgpd_di, argc, argv);
+ frr_opt_add("p:l:rne:", longopts,
+ " -p, --bgp_port Set bgp protocol's port number\n"
+ " -l, --listenon Listen on specified address (implies -n)\n"
+ " -r, --retain When program terminates, retain added route by bgpd.\n"
+ " -n, --no_kernel Do not install route to kernel.\n"
+ " -e, --ecmp Specify ECMP to use.\n");
/* Command line argument treatment. */
- while (1)
+ while (1)
{
- opt = getopt_long (argc, argv, "df:i:z:hp:l:A:P:rnu:g:vCS", longopts, 0);
+ opt = frr_getopt (argc, argv, 0);
if (opt == EOF)
break;
@@ -447,28 +384,13 @@ main (int argc, char **argv)
{
case 0:
break;
- case 'd':
- daemon_mode = 1;
- break;
- case 'f':
- config_file = optarg;
- break;
- case 'i':
- pid_file = optarg;
- break;
- case 'z':
- zclient_serv_path_set (optarg);
- break;
case 'p':
tmp_port = atoi (optarg);
if (tmp_port <= 0 || tmp_port > 0xffff)
- bm->port = BGP_PORT_DEFAULT;
+ bgp_port = BGP_PORT_DEFAULT;
else
bm->port = tmp_port;
break;
- case 'A':
- vty_addr = optarg;
- break;
case 'e':
multipath_num = atoi (optarg);
if (multipath_num > MULTIPATH_NUM || multipath_num <= 0)
@@ -477,107 +399,38 @@ main (int argc, char **argv)
return 1;
}
break;
- case 'P':
- /* Deal with atoi() returning 0 on failure, and bgpd not
- listening on bgp port... */
- if (strcmp(optarg, "0") == 0)
- {
- vty_port = 0;
- break;
- }
- vty_port = atoi (optarg);
- if (vty_port <= 0 || vty_port > 0xffff)
- vty_port = BGP_VTY_PORT;
- break;
- case OPTION_VTYSOCK:
- set_socket_path(vty_sock_path, BGP_VTYSH_PATH, optarg, sizeof (vty_sock_path));
- break;
case 'r':
retain_mode = 1;
break;
case 'l':
- bm->address = optarg;
+ bgp_address = optarg;
/* listenon implies -n */
case 'n':
bgp_option_set (BGP_OPT_NO_FIB);
break;
- case 'u':
- bgpd_privs.user = optarg;
- break;
- case 'g':
- bgpd_privs.group = optarg;
- break;
- case 'S': /* skip run as = override bgpd_privs */
- skip_runas = 1;
- break;
- case 'v':
- print_version (progname);
- exit (0);
- break;
- case 'C':
- dryrun = 1;
- break;
- case 'h':
- usage (progname, 0);
- break;
default:
- usage (progname, 1);
+ frr_help_exit (1);
break;
}
}
- zlog_default = openzlog (progname, ZLOG_BGP, 0,
- LOG_CONS|LOG_NDELAY|LOG_PID, LOG_DAEMON);
-
- if (skip_runas)
- memset (&bgpd_privs, 0, sizeof (bgpd_privs));
- zprivs_init (&bgpd_privs);
-
-#if defined(HAVE_CUMULUS)
- zlog_set_level (NULL, ZLOG_DEST_SYSLOG, zlog_default->default_lvl);
-#endif
+ /* BGP master init. */
+ bgp_master_init (frr_init ());
+ bm->port = bgp_port;
+ bm->address = bgp_address;
/* Initializations. */
- srandom (time (NULL));
- signal_init (bm->master, array_size(bgp_signals), bgp_signals);
- cmd_init (1);
- vty_init (bm->master);
- memory_init ();
bgp_vrf_init ();
/* BGP related initialization. */
bgp_init ();
- /* Parse config file. */
- vty_read_config (config_file, config_default);
-
- /* Start execution only if not in dry-run mode */
- if(dryrun)
- return(0);
-
- /* Turn into daemon if daemon_mode is set. */
- if (daemon_mode && daemon (0, 0) < 0)
- {
- zlog_err("BGPd daemon failed: %s", strerror(errno));
- return (1);
- }
-
-
- /* Process ID file creation. */
- pid_output (pid_file);
-
- /* Make bgp vty socket. */
- vty_serv_sock (vty_addr, vty_port, vty_sock_path);
-
- /* Print banner. */
- zlog_notice ("BGPd %s starting: vty@%d, bgp@%s:%d", FRR_COPYRIGHT,
- vty_port,
- (bm->address ? bm->address : "<all>"),
- bm->port);
+ snprintf (bgpd_di.startinfo, sizeof (bgpd_di.startinfo), ", bgp@%s:%d",
+ (bm->address ? bm->address : "<all>"),
+ bm->port);
- /* Start finite state machine, here we go! */
- while (thread_fetch (bm->master, &thread))
- thread_call (&thread);
+ frr_config_fork ();
+ frr_run (bm->master);
/* Not reached. */
return (0);
diff --git a/bgpd/bgp_updgrp.h b/bgpd/bgp_updgrp.h
index 6ab384369c..8f3e27bc84 100644
--- a/bgpd/bgp_updgrp.h
+++ b/bgpd/bgp_updgrp.h
@@ -249,7 +249,6 @@ struct update_subgroup
u_int32_t merge_checks_triggered;
uint64_t id;
- struct zlog *log;
u_int16_t sflags;
diff --git a/bgpd/bgpd.c b/bgpd/bgpd.c
index 095f0fce6f..9d76b3d58b 100644
--- a/bgpd/bgpd.c
+++ b/bgpd/bgpd.c
@@ -7588,7 +7588,7 @@ bgp_config_write (struct vty *vty)
}
void
-bgp_master_init (void)
+bgp_master_init (struct thread_master *master)
{
qobj_init ();
@@ -7598,7 +7598,7 @@ bgp_master_init (void)
bm->bgp = list_new ();
bm->listen_sockets = list_new ();
bm->port = BGP_PORT_DEFAULT;
- bm->master = thread_master_create ();
+ bm->master = master;
bm->start_time = bgp_clock ();
bm->t_rmap_update = NULL;
bm->rmap_update_timer = RMAP_DEFAULT_UPDATE_TIMER;
diff --git a/bgpd/bgpd.h b/bgpd/bgpd.h
index 99d87d785c..9ccc0e39de 100644
--- a/bgpd/bgpd.h
+++ b/bgpd/bgpd.h
@@ -1218,7 +1218,7 @@ extern char *peer_uptime (time_t, char *, size_t, u_char, json_object *);
extern int bgp_config_write (struct vty *);
extern void bgp_config_write_family_header (struct vty *, afi_t, safi_t, int *);
-extern void bgp_master_init (void);
+extern void bgp_master_init (struct thread_master *master);
extern void bgp_init (void);
extern void bgp_route_map_init (void);
diff --git a/bgpd/rfapi/rfapi_vty.c b/bgpd/rfapi/rfapi_vty.c
index 1f3066b5c5..c52026e8ef 100644
--- a/bgpd/rfapi/rfapi_vty.c
+++ b/bgpd/rfapi/rfapi_vty.c
@@ -29,6 +29,7 @@
#include "lib/memory.h"
#include "lib/routemap.h"
#include "lib/log.h"
+#include "lib/log_int.h"
#include "lib/linklist.h"
#include "lib/command.h"
@@ -352,7 +353,7 @@ rfapiDebugPrintf (void *dummy, const char *format, ...)
{
va_list args;
va_start (args, format);
- vzlog (NULL, LOG_DEBUG, format, args);
+ vzlog (LOG_DEBUG, format, args);
va_end (args);
return 0;
}
@@ -411,7 +412,7 @@ rfapiStream2Vty (
*fp = (int (*)(void *, const char *,...)) rfapiDebugPrintf;
*outstream = NULL;
*vty_newline = str_vty_newline (*vty);
- return (vzlog_test (NULL, LOG_DEBUG));
+ return (vzlog_test (LOG_DEBUG));
}
if (((uintptr_t) stream == (uintptr_t) 1) ||
diff --git a/configure.ac b/configure.ac
index b1d9a1b691..e46e44a8b7 100755
--- a/configure.ac
+++ b/configure.ac
@@ -80,6 +80,7 @@ CFLAGS="$orig_cflags"
AC_PROG_CC_C99
AC_PROG_EGREP
+PKG_PROG_PKG_CONFIG
dnl autoconf 2.59 appears not to support AC_PROG_SED
dnl AC_PROG_SED
@@ -214,6 +215,8 @@ AC_ARG_ENABLE(ospf6d,
AS_HELP_STRING([--disable-ospf6d], [do not build ospf6d]))
AC_ARG_ENABLE(ldpd,
AS_HELP_STRING([--enable-ldpd], [build ldpd]))
+AC_ARG_ENABLE(nhrpd,
+ AS_HELP_STRING([--disable-nhrpd], [do not build nhrpd]))
AC_ARG_ENABLE(watchfrr,
AS_HELP_STRING([--disable-watchfrr], [do not build watchfrr]))
AC_ARG_ENABLE(isisd,
@@ -1185,6 +1188,18 @@ else
fi
AM_CONDITIONAL(LDPD, test "x$LDPD" = "xldpd")
+NHRPD=""
+if test "$opsys" = "gnu-linux"; then
+ if test "${enable_nhrpd}" != "no"; then
+ NHRPD="nhrpd"
+ fi
+else
+ if test "${enable_nhrpd}" = "yes"; then
+ AC_MSG_ERROR([nhrpd requires kernel APIs that are only present on Linux.])
+ fi
+fi
+AM_CONDITIONAL(NHRPD, test "x$NHRPD" = "xnhrpd")
+
if test "${enable_watchfrr}" = "no";then
WATCHFRR=""
else
@@ -1265,6 +1280,7 @@ AC_SUBST(RIPNGD)
AC_SUBST(OSPFD)
AC_SUBST(OSPF6D)
AC_SUBST(LDPD)
+AC_SUBST(NHRPD)
AC_SUBST(WATCHFRR)
AC_SUBST(ISISD)
AC_SUBST(PIMD)
@@ -1286,6 +1302,14 @@ fi
AC_SUBST(HAVE_LIBPCREPOSIX)
dnl ------------------
+dnl check C-Ares library
+dnl ------------------
+if test "${NHRPD}" != ""; then
+ PKG_CHECK_MODULES([CARES], [libcares])
+fi
+
+
+dnl ------------------
dnl check Net-SNMP library
dnl ------------------
if test "${enable_snmp}" != ""; then
@@ -1604,28 +1628,8 @@ fi
AC_MSG_RESULT(${frr_statedir})
AC_SUBST(frr_statedir)
-AC_DEFINE_UNQUOTED(PATH_ZEBRA_PID, "$frr_statedir/zebra.pid",zebra PID)
-AC_DEFINE_UNQUOTED(PATH_RIPD_PID, "$frr_statedir/ripd.pid",ripd PID)
-AC_DEFINE_UNQUOTED(PATH_RIPNGD_PID, "$frr_statedir/ripngd.pid",ripngd PID)
-AC_DEFINE_UNQUOTED(PATH_BGPD_PID, "$frr_statedir/bgpd.pid",bgpd PID)
-AC_DEFINE_UNQUOTED(PATH_OSPFD_PID, "$frr_statedir/ospfd.pid",ospfd PID)
-AC_DEFINE_UNQUOTED(PATH_OSPF6D_PID, "$frr_statedir/ospf6d.pid",ospf6d PID)
-AC_DEFINE_UNQUOTED(PATH_LDPD_PID, "$frr_statedir/ldpd.pid",ldpd PID)
AC_DEFINE_UNQUOTED(LDPD_SOCKET, "$frr_statedir/ldpd.sock",ldpd control socket)
-AC_DEFINE_UNQUOTED(PATH_ISISD_PID, "$frr_statedir/isisd.pid",isisd PID)
-AC_DEFINE_UNQUOTED(PATH_PIMD_PID, "$frr_statedir/pimd.pid",pimd PID)
-AC_DEFINE_UNQUOTED(PATH_WATCHFRR_PID, "$frr_statedir/watchfrr.pid",watchfrr PID)
AC_DEFINE_UNQUOTED(ZEBRA_SERV_PATH, "$frr_statedir/zserv.api",zebra api socket)
-AC_DEFINE_UNQUOTED(ZEBRA_VTYSH_PATH, "$frr_statedir/zebra.vty",zebra vty socket)
-AC_DEFINE_UNQUOTED(RIP_VTYSH_PATH, "$frr_statedir/ripd.vty",rip vty socket)
-AC_DEFINE_UNQUOTED(RIPNG_VTYSH_PATH, "$frr_statedir/ripngd.vty",ripng vty socket)
-AC_DEFINE_UNQUOTED(BGP_VTYSH_PATH, "$frr_statedir/bgpd.vty",bgpd vty socket)
-AC_DEFINE_UNQUOTED(OSPF_VTYSH_PATH, "$frr_statedir/ospfd.vty",ospfd vty socket)
-AC_DEFINE_UNQUOTED(OSPF6_VTYSH_PATH, "$frr_statedir/ospf6d.vty",ospf6d vty socket)
-AC_DEFINE_UNQUOTED(LDP_VTYSH_PATH, "$frr_statedir/ldpd.vty",ldpd vty socket)
-AC_DEFINE_UNQUOTED(ISIS_VTYSH_PATH, "$frr_statedir/isisd.vty",isisd vty socket)
-AC_DEFINE_UNQUOTED(PIM_VTYSH_PATH, "$frr_statedir/pimd.vty",pimd vty socket)
-AC_DEFINE_UNQUOTED(WATCHFRR_VTYSH_PATH, "$frr_statedir/watchfrr.vty",watchfrr vty socket)
AC_DEFINE_UNQUOTED(DAEMON_VTY_DIR, "$frr_statedir",daemon vty directory)
dnl autoconf does this, but it does it too late...
@@ -1667,6 +1671,7 @@ AC_CONFIG_FILES([Makefile lib/Makefile qpb/Makefile zebra/Makefile ripd/Makefile
ospf6d/Makefile ldpd/Makefile isisd/Makefile vtysh/Makefile
doc/Makefile ospfclient/Makefile tests/Makefile m4/Makefile
pimd/Makefile
+ nhrpd/Makefile
redhat/Makefile
tools/Makefile
cumulus/Makefile
@@ -1686,6 +1691,7 @@ AC_CONFIG_FILES([Makefile lib/Makefile qpb/Makefile zebra/Makefile ripd/Makefile
doc/ripd.8
doc/ripngd.8
doc/pimd.8
+ doc/nhrpd.8
doc/vtysh.1
doc/watchfrr.8
doc/zebra.8
diff --git a/cumulus/etc/frr/daemons b/cumulus/etc/frr/daemons
index 91e2a0f5b6..17dfc92ec4 100644
--- a/cumulus/etc/frr/daemons
+++ b/cumulus/etc/frr/daemons
@@ -30,3 +30,4 @@ ripngd=no
isisd=no
pimd=no
ldpd=no
+nhrpd=no
diff --git a/cumulus/etc/frr/debian.conf b/cumulus/etc/frr/debian.conf
index a4945f39ae..eed8379e8c 100644
--- a/cumulus/etc/frr/debian.conf
+++ b/cumulus/etc/frr/debian.conf
@@ -13,6 +13,7 @@ ripngd_options=" --daemon -A ::1"
isisd_options=" --daemon -A 127.0.0.1"
pimd_options=" --daemon -A 127.0.0.1"
ldpd_options=" --daemon -A 127.0.0.1"
+nhrpd_options=" --daemon -A 127.0.0.1"
# The list of daemons to watch is automatically generated by the init script.
watchfrr_enable=yes
diff --git a/doc/Makefile.am b/doc/Makefile.am
index 04389c63ac..d82a307304 100644
--- a/doc/Makefile.am
+++ b/doc/Makefile.am
@@ -59,7 +59,9 @@ frr.pdf: $(info_TEXINFOS) $(figures_pdf) $(frr_TEXINFOS)
frr_TEXINFOS = appendix.texi basic.texi bgpd.texi isisd.texi filter.texi \
vnc.texi \
- install.texi ipv6.texi kernel.texi main.texi ospf6d.texi ospfd.texi \
+ install.texi ipv6.texi kernel.texi main.texi \
+ nhrpd.texi \
+ ospf6d.texi ospfd.texi \
overview.texi protocol.texi ripd.texi ripngd.texi routemap.texi \
snmp.texi vtysh.texi routeserver.texi defines.texi $(figures_png) \
snmptrap.texi ospf_fundamentals.texi isisd.texi $(figures_txt)
@@ -111,6 +113,10 @@ if RIPNGD
man_MANS += ripngd.8
endif
+if NHRPD
+man_MANS += nhrpd.8
+endif
+
if VTYSH
man_MANS += vtysh.1
endif
@@ -134,6 +140,7 @@ EXTRA_DIST = BGP-TypeCode draft-zebra-00.ms draft-zebra-00.txt \
ripd.8.in \
ripngd.8.in \
pimd.8.in \
+ nhrpd.8.in \
vtysh.1.in \
watchfrr.8.in \
zebra.8.in \
diff --git a/doc/bgpd.8.in b/doc/bgpd.8.in
index 7047744635..9026f2cdee 100644
--- a/doc/bgpd.8.in
+++ b/doc/bgpd.8.in
@@ -108,6 +108,7 @@ debugging options, see the Info file, or the source for details.
.BR ospfd (8),
.BR ospf6d (8),
.BR isisd (8),
+.BR nhrpd (8),
.BR zebra (8),
.BR vtysh (1)
.SH BUGS
diff --git a/doc/frr.texi b/doc/frr.texi
index d4a78b7b81..a64dc9e729 100644
--- a/doc/frr.texi
+++ b/doc/frr.texi
@@ -90,6 +90,7 @@ for @value{PACKAGE_STRING}. @uref{http://www.freerangerouting.org,,Frr} is a for
* OSPFv2::
* OSPFv3::
* ISIS::
+* NHRP::
* BGP::
* Configuring Frr as a Route Server::
* VNC and VNC-GW::
@@ -116,6 +117,7 @@ for @value{PACKAGE_STRING}. @uref{http://www.freerangerouting.org,,Frr} is a for
@include ospfd.texi
@include ospf6d.texi
@include isisd.texi
+@include nhrpd.texi
@include bgpd.texi
@include routeserver.texi
@include vnc.texi
diff --git a/doc/install.texi b/doc/install.texi
index 8c501ed45d..b34f316844 100644
--- a/doc/install.texi
+++ b/doc/install.texi
@@ -267,6 +267,7 @@ bgpd 2605/tcp # BGPd vty
ospf6d 2606/tcp # OSPF6d vty
ospfapi 2607/tcp # ospfapi
isisd 2608/tcp # ISISd vty
+nhrpd 2610/tcp # nhrpd vty
pimd 2611/tcp # PIMd vty
@end example
diff --git a/doc/nhrpd.8.in b/doc/nhrpd.8.in
new file mode 100644
index 0000000000..c5e4f7e324
--- /dev/null
+++ b/doc/nhrpd.8.in
@@ -0,0 +1,105 @@
+.TH NHRP 8 "24 January 2017" "@PACKAGE_FULLNAME@ NHRP daemon" "Version @PACKAGE_VERSION@"
+.SH NAME
+nhrpd \- a Next Hop Routing Protocol routing engine for use with @PACKAGE_FULLNAME@.
+.SH SYNOPSIS
+.B nhrpd
+[
+.B \-dhv
+] [
+.B \-f
+.I config-file
+] [
+.B \-i
+.I pid-file
+] [
+.B \-P
+.I port-number
+] [
+.B \-A
+.I vty-address
+] [
+.B \-u
+.I user
+] [
+.B \-g
+.I group
+]
+.SH DESCRIPTION
+.B nhrpd
+is a routing component that works with the
+.B @PACKAGE_FULLNAME@
+routing engine.
+.SH OPTIONS
+Options available for the
+.B nhrpd
+command:
+.TP
+\fB\-d\fR, \fB\-\-daemon\fR
+Runs in daemon mode, forking and exiting from tty.
+.TP
+\fB\-f\fR, \fB\-\-config-file \fR\fIconfig-file\fR
+Specifies the config file to use for startup. If not specified this
+option will likely default to \fB\fI@CFG_SYSCONF@/nhrpd.conf\fR.
+.TP
+\fB\-g\fR, \fB\-\-group \fR\fIgroup\fR
+Specify the group to run as. Default is \fI@enable_group@\fR.
+.TP
+\fB\-h\fR, \fB\-\-help\fR
+A brief message.
+.TP
+\fB\-i\fR, \fB\-\-pid_file \fR\fIpid-file\fR
+When nhrpd starts its process identifier is written to
+\fB\fIpid-file\fR. The init system uses the recorded PID to stop or
+restart nhrpd. The likely default is \fB\fI@CFG_STATE@/nhrpd.pid\fR.
+.TP
+\fB\-P\fR, \fB\-\-vty_port \fR\fIport-number\fR
+Specify the port that the nhrpd VTY will listen on. This defaults to
+2610, as specified in \fB\fI/etc/services\fR.
+.TP
+\fB\-A\fR, \fB\-\-vty_addr \fR\fIvty-address\fR
+Specify the address that the nhrpd VTY will listen on. Default is all
+interfaces.
+.TP
+\fB\-u\fR, \fB\-\-user \fR\fIuser\fR
+Specify the user to run as. Default is \fI@enable_user@\fR.
+.TP
+\fB\-v\fR, \fB\-\-version\fR
+Print the version and exit.
+.SH FILES
+.TP
+.BI @CFG_SBIN@/nhrpd
+The default location of the
+.B nhrpd
+binary.
+.TP
+.BI @CFG_SYSCONF@/nhrpd.conf
+The default location of the
+.B nhrpd
+config file.
+.TP
+.BI $(PWD)/nhrpd.log
+If the
+.B nhrpd
+process is config'd to output logs to a file, then you will find this
+file in the directory where you started \fBnhrpd\fR.
+.SH WARNING
+This man page is intended to be a quick reference for command line
+options. The definitive document is the Info file \fB@PACKAGE_NAME@\fR.
+.SH DIAGNOSTICS
+The nhrpd process may log to standard output, to a VTY, to a log
+file, or through syslog to the system logs. \fBnhrpd\fR supports many
+debugging options, see the Info file, or the source for details.
+.SH "SEE ALSO"
+.BR bgpd (8),
+.BR ripd (8),
+.BR ripngd (8),
+.BR ospfd (8),
+.BR ospf6d (8),
+.BR zebra (8),
+.BR vtysh (1)
+
+.B nhrpd
+eats bugs for breakfast. If you have food for the maintainers try
+.BI @PACKAGE_BUGREPORT@
+.SH AUTHORS
+Timo Teräs <timo.teras@iki.fi>
diff --git a/doc/nhrpd.texi b/doc/nhrpd.texi
new file mode 100644
index 0000000000..1820044aea
--- /dev/null
+++ b/doc/nhrpd.texi
@@ -0,0 +1,143 @@
+@cindex NHRP
+@node NHRP
+@chapter NHRP
+
+@command{nhrpd} is a daemon to support Next Hop Routing Protocol (NHRP).
+NHRP is described in RFC2332.
+
+NHRP is used to improve the efficiency of routing computer network
+traffic over Non-Broadcast, Multiple Access (NBMA) Networks. NHRP provides
+an ARP-like solution that allows a system to dynamically learn the NBMA
+address of the other systems that are part of that network, allowing
+these systems to directly communicate without requiring traffic to use
+an intermediate hop.
+
+Cisco Dynamic Multipoint VPN (DMVPN) is based on NHRP, and
+@value{PACKAGE_NAME} nhrpd implements this scenario.
+
+@menu
+* Routing Design::
+* Configuring NHRP::
+* Hub Functionality::
+* Integration with IKE::
+* NHRP Events::
+* Configuration Example::
+@end menu
+
+@node Routing Design
+@section Routing Design
+
+nhrpd never handles routing of prefixes itself. You need to run some
+real routing protocol (e.g. BGP) to advertise routes over the tunnels.
+What nhrpd does it establishes 'shortcut routes' that optimizes the
+routing protocol to avoid going through extra nodes in NBMA GRE mesh.
+
+nhrpd does route NHRP domain addresses individually using per-host prefixes.
+This is similar to Cisco FlexVPN; but in contrast to opennhrp which uses
+a generic subnet route.
+
+To create NBMA GRE tunnel you might use the following (linux terminal
+commands):
+@example
+@group
+ ip tunnel add gre1 mode gre key 42 ttl 64
+ ip addr add 10.255.255.2/32 dev gre1
+ ip link set gre1 up
+@end group
+@end example
+
+Note that the IP-address is assigned as host prefix to gre1. nhrpd will
+automatically create additional host routes pointing to gre1 when
+a connection with these hosts is established.
+
+The gre1 subnet prefix should be announced by routing protocol from the
+hub nodes (e.g. BGP 'network' announce). This allows the routing protocol
+to decide which is the closest hub and determine the relay hub on prefix
+basis when direct tunnel is not established.
+
+nhrpd will redistribute directly connected neighbors to zebra. Within
+hub nodes, these routes should be internally redistributed using some
+routing protocol (e.g. iBGP) to allow hubs to be able to relay all traffic.
+
+This can be achieved in hubs with the following bgp configuration (network
+command defines the GRE subnet):
+@example
+@group
+router bgp 65555
+ network 172.16.0.0/16
+ redistribute nhrp
+@end group
+@end example
+
+
+@node Configuring NHRP
+@section Configuring NHRP
+
+FIXME
+
+@node Hub Functionality
+@section Hub Functionality
+
+In addition to routing nhrp redistributed host prefixes, the hub nodes
+are also responsible to send NHRP Traffic Indication messages that
+trigger creation of the shortcut tunnels.
+
+nhrpd sends Traffic Indication messages based on network traffic captured
+using NFLOG. Typically you want to send Traffic Indications for network
+traffic that is routed from gre1 back to gre1 in rate limited manner.
+This can be achieved with the following iptables rule.
+
+@example
+@group
+iptables -A FORWARD -i gre1 -o gre1 \
+ -m hashlimit --hashlimit-upto 4/minute --hashlimit-burst 1 \
+ --hashlimit-mode srcip,dstip --hashlimit-srcmask 24 --hashlimit-dstmask 24 \
+ --hashlimit-name loglimit-0 -j NFLOG --nflog-group 1 --nflog-range 128
+@end group
+@end example
+
+You can fine tune the src/dstmask according to the prefix lengths you
+announce internal, add additional IP range matches, or rate limitation
+if needed. However, the above should be good in most cases.
+
+This kernel NFLOG target's nflog-group is configured in global nhrp config
+with:
+@example
+@group
+nhrp nflog-group 1
+@end group
+@end example
+
+To start sending these traffic notices out from hubs, use the nhrp
+per-interface directive:
+@example
+@group
+interface gre1
+ ip nhrp redirect
+@end group
+@end example
+
+@node Integration with IKE
+@section Integration with IKE
+
+nhrpd needs tight integration with IKE daemon for various reasons.
+Currently only strongSwan is supported as IKE daemon.
+
+nhrpd connects to strongSwan using VICI protocol based on UNIX socket
+(hardcoded now as /var/run/charon.vici).
+
+strongSwan currently needs few patches applied. Please check out the
+@uref{http://git.alpinelinux.org/cgit/user/tteras/strongswan/log/?h=tteras-release,release}
+and
+@uref{http://git.alpinelinux.org/cgit/user/tteras/strongswan/log/?h=tteras,working tree}
+git repositories for the patches.
+
+@node NHRP Events
+@section NHRP Events
+
+FIXME
+
+@node Configuration Example
+@section Configuration Example
+
+FIXME
diff --git a/doc/zebra.8.in b/doc/zebra.8.in
index e7d00e10a3..4599a85635 100644
--- a/doc/zebra.8.in
+++ b/doc/zebra.8.in
@@ -119,6 +119,7 @@ debugging options, see the Info file, or the source for details.
.BR ospfd (8),
.BR ospf6d (8),
.BR isisd (8),
+.BR nhrpd (8),
.BR vtysh (1)
.SH BUGS
.B zebra
diff --git a/isisd/isis_main.c b/isisd/isis_main.c
index 1b9fa68051..a6ad424ace 100644
--- a/isisd/isis_main.c
+++ b/isisd/isis_main.c
@@ -38,8 +38,8 @@
#include "plist.h"
#include "zclient.h"
#include "vrf.h"
-#include "sockopt.h"
#include "qobj.h"
+#include "libfrr.h"
#include "isisd/dict.h"
#include "isisd/isis_constants.h"
@@ -82,41 +82,13 @@ struct zebra_privs_t isisd_privs = {
};
/* isisd options */
-#define OPTION_VTYSOCK 1000
struct option longopts[] = {
- {"daemon", no_argument, NULL, 'd'},
- {"config_file", required_argument, NULL, 'f'},
- {"pid_file", required_argument, NULL, 'i'},
- {"socket", required_argument, NULL, 'z'},
- {"vty_addr", required_argument, NULL, 'A'},
- {"vty_port", required_argument, NULL, 'P'},
- {"vty_socket", required_argument, NULL, OPTION_VTYSOCK},
- {"user", required_argument, NULL, 'u'},
- {"group", required_argument, NULL, 'g'},
- {"version", no_argument, NULL, 'v'},
- {"dryrun", no_argument, NULL, 'C'},
- {"help", no_argument, NULL, 'h'},
{0}
};
-/* Configuration file and directory. */
-char config_default[] = SYSCONFDIR ISISD_DEFAULT_CONFIG;
-char *config_file = NULL;
-
-/* isisd program name. */
-char *progname;
-
-/* VTY Socket prefix */
-char vty_sock_path[MAXPATHLEN] = ISIS_VTYSH_PATH;
-
-int daemon_mode = 0;
-
/* Master of threads. */
struct thread_master *master;
-/* Process ID saved for use by init system */
-const char *pid_file = PATH_ISISD_PID;
-
/* for reload */
char _cwd[MAXPATHLEN];
char _progpath[MAXPATHLEN];
@@ -134,36 +106,6 @@ void sigterm(void);
void sigusr1(void);
-/* Help information display. */
-static void
-usage (int status)
-{
- if (status != 0)
- fprintf (stderr, "Try `%s --help' for more information.\n", progname);
- else
- {
- printf ("Usage : %s [OPTION...]\n\n\
-Daemon which manages IS-IS routing\n\n\
--d, --daemon Runs in daemon mode\n\
--f, --config_file Set configuration file name\n\
--i, --pid_file Set process identifier file name\n\
--z, --socket Set path of zebra socket\n\
--A, --vty_addr Set vty's bind address\n\
--P, --vty_port Set vty's port number\n\
- --vty_socket Override vty socket path\n\
--u, --user User to run as\n\
--g, --group Group to run as\n\
--v, --version Print program version\n\
--C, --dryrun Check configuration for validity and exit\n\
--h, --help Display this help and exit\n\
-\n\
-Report bugs to %s\n", progname, FRR_BUG_ADDRESS);
- }
-
- exit (status);
-}
-
-
void
reload ()
{
@@ -213,7 +155,7 @@ void
sigusr1 (void)
{
zlog_debug ("SIGUSR1 received");
- zlog_rotate (NULL);
+ zlog_rotate();
}
struct quagga_signal_t isisd_signals[] =
@@ -236,28 +178,26 @@ struct quagga_signal_t isisd_signals[] =
},
};
+FRR_DAEMON_INFO(isisd, ISIS,
+ .vty_port = ISISD_VTY_PORT,
+
+ .proghelp = "Implementation of the IS-IS routing protocol.",
+ .copyright = "Copyright (c) 2001-2002 Sampo Saaristo,"
+ " Ofer Wald and Hannes Gredler",
+
+ .signals = isisd_signals,
+ .n_signals = array_size(isisd_signals),
+
+ .privs = &isisd_privs,
+)
+
/*
* Main routine of isisd. Parse arguments and handle IS-IS state machine.
*/
int
main (int argc, char **argv, char **envp)
{
- char *p;
- int opt, vty_port = ISISD_VTY_PORT;
- struct thread thread;
- char *config_file = NULL;
- char *vty_addr = NULL;
- int dryrun = 0;
-
- /* Get the programname without the preceding path. */
- progname = ((p = strrchr (argv[0], '/')) ? ++p : argv[0]);
-
- zlog_default = openzlog (progname, ZLOG_ISIS, 0,
- LOG_CONS | LOG_NDELAY | LOG_PID, LOG_DAEMON);
- zprivs_init (&isisd_privs);
-#if defined(HAVE_CUMULUS)
- zlog_set_level (NULL, ZLOG_DEST_SYSLOG, zlog_default->default_lvl);
-#endif
+ int opt;
/* for reload */
_argc = argc;
@@ -274,10 +214,13 @@ main (int argc, char **argv, char **envp)
else
snprintf (_progpath, sizeof (_progpath), "%s", argv[0]);
+ frr_preinit (&isisd_di, argc, argv);
+ frr_opt_add ("", longopts, "");
+
/* Command line argument treatment. */
while (1)
{
- opt = getopt_long (argc, argv, "df:i:z:hA:p:P:u:g:vC", longopts, 0);
+ opt = frr_getopt (argc, argv, NULL);
if (opt == EOF)
break;
@@ -286,74 +229,19 @@ main (int argc, char **argv, char **envp)
{
case 0:
break;
- case 'd':
- daemon_mode = 1;
- break;
- case 'f':
- config_file = optarg;
- break;
- case 'i':
- pid_file = optarg;
- break;
- case 'z':
- zclient_serv_path_set (optarg);
- break;
- case 'A':
- vty_addr = optarg;
- break;
- case 'P':
- /* Deal with atoi() returning 0 on failure, and isisd not
- listening on isisd port... */
- if (strcmp (optarg, "0") == 0)
- {
- vty_port = 0;
- break;
- }
- vty_port = atoi (optarg);
- vty_port = (vty_port ? vty_port : ISISD_VTY_PORT);
- break;
- case OPTION_VTYSOCK:
- set_socket_path(vty_sock_path, ISIS_VTYSH_PATH, optarg, sizeof (vty_sock_path));
- break;
- case 'u':
- isisd_privs.user = optarg;
- break;
- case 'g':
- isisd_privs.group = optarg;
- break;
- case 'v':
- printf ("ISISd version %s\n", ISISD_VERSION);
- printf ("Copyright (c) 2001-2002 Sampo Saaristo,"
- " Ofer Wald and Hannes Gredler\n");
- print_version ("Zebra");
- exit (0);
- break;
- case 'C':
- dryrun = 1;
- break;
- case 'h':
- usage (0);
- break;
default:
- usage (1);
+ frr_help_exit (1);
break;
}
}
+ vty_config_lockless ();
/* thread master */
- master = thread_master_create ();
-
- /* random seed from time */
- srandom (time (NULL));
+ master = frr_init ();
/*
* initializations
*/
- signal_init (master, array_size (isisd_signals), isisd_signals);
- cmd_init (1);
- vty_config_lockless ();
- vty_init (master);
- memory_init ();
access_list_init();
vrf_init ();
prefix_list_init();
@@ -369,34 +257,8 @@ main (int argc, char **argv, char **envp)
isis_zebra_init(master);
- /* parse config file */
- /* this is needed three times! because we have interfaces before the areas */
- vty_read_config (config_file, config_default);
-
- /* Start execution only if not in dry-run mode */
- if (dryrun)
- return(0);
-
- /* demonize */
- if (daemon_mode && daemon (0, 0) < 0)
- {
- zlog_err("ISISd daemon failed: %s", strerror(errno));
- return (1);
- }
-
- /* Process ID file creation. */
- if (pid_file[0] != '\0')
- pid_output (pid_file);
-
- /* Make isis vty socket. */
- vty_serv_sock (vty_addr, vty_port, vty_sock_path);
-
- /* Print banner. */
- zlog_notice ("Quagga-ISISd %s starting: vty@%d", FRR_VERSION, vty_port);
-
- /* Start finite state machine. */
- while (thread_fetch (master, &thread))
- thread_call (&thread);
+ frr_config_fork ();
+ frr_run (master);
/* Not reached. */
exit (0);
diff --git a/ldpd/ldpd.c b/ldpd/ldpd.c
index 4e79f8b8ab..98ea5ca53e 100644
--- a/ldpd/ldpd.c
+++ b/ldpd/ldpd.c
@@ -40,8 +40,8 @@
#include "zclient.h"
#include "vrf.h"
#include "filter.h"
-#include "sockopt.h"
#include "qobj.h"
+#include "libfrr.h"
static void ldpd_shutdown(void);
static pid_t start_child(enum ldpd_process, char *, int, int,
@@ -90,12 +90,6 @@ static pid_t lde_pid;
/* Master of threads. */
struct thread_master *master;
-/* Process ID saved for use by init system */
-static const char *pid_file = PATH_LDPD_PID;
-
-/* Configuration filename and directory. */
-static char config_default[] = SYSCONFDIR LDP_DEFAULT_CONFIG;
-
/* ldpd privileges */
static zebra_capabilities_t _caps_p [] =
{
@@ -117,63 +111,17 @@ struct zebra_privs_t ldpd_privs =
.cap_num_i = 0
};
-/* VTY Socket prefix */
-char vty_sock_path[MAXPATHLEN] = LDP_VTYSH_PATH;
-
/* CTL Socket path */
char ctl_sock_path[MAXPATHLEN] = LDPD_SOCKET;
/* LDPd options. */
-#define OPTION_VTYSOCK 1000
#define OPTION_CTLSOCK 1001
static struct option longopts[] =
{
- { "daemon", no_argument, NULL, 'd'},
- { "config_file", required_argument, NULL, 'f'},
- { "pid_file", required_argument, NULL, 'i'},
- { "socket", required_argument, NULL, 'z'},
- { "dryrun", no_argument, NULL, 'C'},
- { "help", no_argument, NULL, 'h'},
- { "vty_addr", required_argument, NULL, 'A'},
- { "vty_port", required_argument, NULL, 'P'},
- { "vty_socket", required_argument, NULL, OPTION_VTYSOCK},
{ "ctl_socket", required_argument, NULL, OPTION_CTLSOCK},
- { "user", required_argument, NULL, 'u'},
- { "group", required_argument, NULL, 'g'},
- { "version", no_argument, NULL, 'v'},
{ 0 }
};
-/* Help information display. */
-static void __attribute__ ((noreturn))
-usage(char *progname, int status)
-{
- if (status != 0)
- fprintf(stderr, "Try `%s --help' for more information.\n",
- progname);
- else {
- printf("Usage : %s [OPTION...]\n\
-Daemon which manages LDP.\n\n\
--d, --daemon Runs in daemon mode\n\
--f, --config_file Set configuration file name\n\
--i, --pid_file Set process identifier file name\n\
--z, --socket Set path of zebra socket\n\
--A, --vty_addr Set vty's bind address\n\
--P, --vty_port Set vty's port number\n\
- --vty_socket Override vty socket path\n\
- --ctl_socket Override ctl socket path\n\
--u, --user User to run as\n\
--g, --group Group to run as\n\
--v, --version Print program version\n\
--C, --dryrun Check configuration for validity and exit\n\
--h, --help Display this help and exit\n\
-\n\
-Report bugs to %s\n", progname, FRR_BUG_ADDRESS);
- }
-
- exit(status);
-}
-
/* SIGHUP handler. */
static void
sighup(void)
@@ -193,7 +141,7 @@ sigint(void)
static void
sigusr1(void)
{
- zlog_rotate(NULL);
+ zlog_rotate();
}
static struct quagga_signal_t ldp_signals[] =
@@ -216,6 +164,17 @@ static struct quagga_signal_t ldp_signals[] =
}
};
+FRR_DAEMON_INFO(ldpd, LDP,
+ .vty_port = LDP_VTY_PORT,
+
+ .proghelp = "Implementation of the LDP protocol.",
+
+ .signals = ldp_signals,
+ .n_signals = array_size(ldp_signals),
+
+ .privs = &ldpd_privs,
+)
+
int
main(int argc, char *argv[])
{
@@ -223,36 +182,25 @@ main(int argc, char *argv[])
int lflag = 0, eflag = 0;
int pipe_parent2ldpe[2], pipe_parent2ldpe_sync[2];
int pipe_parent2lde[2], pipe_parent2lde_sync[2];
- char *p;
- char *vty_addr = NULL;
- int vty_port = LDP_VTY_PORT;
char *ctl_sock_custom_path = NULL;
char *ctl_sock_name;
- int daemon_mode = 0;
const char *user = NULL;
const char *group = NULL;
- char *config_file = NULL;
- char *progname;
- struct thread thread;
- int dryrun = 0;
ldpd_process = PROC_MAIN;
- /* Set umask before anything for security */
- umask(0027);
-
- /* get program name */
- progname = ((p = strrchr(argv[0], '/')) ? ++p : argv[0]);
-
saved_argv0 = argv[0];
if (saved_argv0 == NULL)
saved_argv0 = (char *)"ldpd";
+ frr_preinit(&ldpd_di, argc, argv);
+ frr_opt_add("LE", longopts,
+ " --ctl_socket Override ctl socket path\n");
+
while (1) {
int opt;
- opt = getopt_long(argc, argv, "df:i:z:hA:P:u:g:vCLE",
- longopts, 0);
+ opt = frr_getopt(argc, argv, NULL);
if (opt == EOF)
break;
@@ -260,37 +208,6 @@ main(int argc, char *argv[])
switch (opt) {
case 0:
break;
- case 'd':
- daemon_mode = 1;
- break;
- case 'f':
- config_file = optarg;
- break;
- case 'A':
- vty_addr = optarg;
- break;
- case 'i':
- pid_file = optarg;
- break;
- case 'z':
- zclient_serv_path_set(optarg);
- break;
- case 'P':
- /*
- * Deal with atoi() returning 0 on failure, and ldpd
- * not listening on ldpd port.
- */
- if (strcmp(optarg, "0") == 0) {
- vty_port = 0;
- break;
- }
- vty_port = atoi(optarg);
- if (vty_port <= 0 || vty_port > 0xffff)
- vty_port = LDP_VTY_PORT;
- break;
- case OPTION_VTYSOCK:
- set_socket_path(vty_sock_path, LDP_VTYSH_PATH, optarg, sizeof (vty_sock_path));
- break;
case OPTION_CTLSOCK:
ctl_sock_name = strrchr(LDPD_SOCKET, '/');
if (ctl_sock_name)
@@ -310,22 +227,6 @@ main(int argc, char *argv[])
strlcat(ctl_sock_path, ctl_sock_name,
sizeof(ctl_sock_path));
break;
- case 'u':
- user = optarg;
- break;
- case 'g':
- group = optarg;
- break;
- case 'v':
- print_version(progname);
- exit(0);
- break;
- case 'C':
- dryrun = 1;
- break;
- case 'h':
- usage(progname, 0);
- break;
case 'L':
lflag = 1;
break;
@@ -333,24 +234,27 @@ main(int argc, char *argv[])
eflag = 1;
break;
default:
- usage(progname, 1);
+ frr_help_exit(1);
break;
}
}
+ user = ldpd_privs.user;
+ group = ldpd_privs.group;
+
argc -= optind;
argv += optind;
if (argc > 0 || (lflag && eflag))
- usage(progname, 1);
+ frr_help_exit(1);
/* check for root privileges */
if (geteuid() != 0) {
errno = EPERM;
- perror(progname);
+ perror(ldpd_di.progname);
exit(1);
}
- zlog_default = openzlog(progname, ZLOG_LDP, 0,
+ openzlog(ldpd_di.progname, "LDP", 0,
LOG_CONS | LOG_NDELAY | LOG_PID, LOG_DAEMON);
if (lflag)
@@ -371,19 +275,11 @@ main(int argc, char *argv[])
/* Get configuration file. */
ldpd_conf = config_new_empty();
ldp_config_reset_main(ldpd_conf, NULL);
- vty_read_config(config_file, config_default);
- /* Start execution only if not in dry-run mode */
- if (dryrun)
- exit(0);
+ frr_config_fork();
QOBJ_REG (ldpd_conf, ldpd_conf);
- if (daemon_mode && daemon(0, 0) < 0) {
- log_warn("LDPd daemon failed");
- exit(1);
- }
-
if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, pipe_parent2ldpe) == -1)
fatal("socketpair");
if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC,
@@ -418,10 +314,6 @@ main(int argc, char *argv[])
user, group, ctl_sock_custom_path);
/* drop privileges */
- if (user)
- ldpd_privs.user = user;
- if (group)
- ldpd_privs.group = group;
zprivs_init(&ldpd_privs);
/* setup signal handler */
@@ -471,18 +363,7 @@ main(int argc, char *argv[])
if (ldpd_conf->ipv6.flags & F_LDPD_AF_ENABLED)
main_imsg_send_net_sockets(AF_INET6);
- /* Process id file create. */
- pid_output(pid_file);
-
- /* Create VTY socket */
- vty_serv_sock(vty_addr, vty_port, vty_sock_path);
-
- /* Print banner. */
- log_notice("LDPd %s starting: vty@%d", FRR_VERSION, vty_port);
-
- /* Fetch next active thread. */
- while (thread_fetch(master, &thread))
- thread_call(&thread);
+ frr_run(master);
/* NOTREACHED */
return (0);
@@ -526,7 +407,7 @@ ldpd_shutdown(void)
ldp_zebra_destroy();
zprivs_terminate(&ldpd_privs);
thread_master_free(master);
- closezlog(zlog_default);
+ closezlog();
exit(0);
}
diff --git a/ldpd/log.c b/ldpd/log.c
index b30604db0d..407668bb03 100644
--- a/ldpd/log.c
+++ b/ldpd/log.c
@@ -24,6 +24,7 @@
#include "log.h"
#include <lib/log.h>
+#include <lib/log_int.h>
#include "mpls.h"
static const char * const procnames[] = {
@@ -59,7 +60,7 @@ vlog(int pri, const char *fmt, va_list ap)
ldpe_imsg_compose_parent(IMSG_LOG, pri, buf, strlen(buf) + 1);
break;
case PROC_MAIN:
- vzlog(NULL, pri, fmt, ap);
+ vzlog(pri, fmt, ap);
break;
}
}
diff --git a/lib/Makefile.am b/lib/Makefile.am
index a9fe646938..1a8c7af42b 100644
--- a/lib/Makefile.am
+++ b/lib/Makefile.am
@@ -29,6 +29,7 @@ libfrr_la_SOURCES = \
grammar_sandbox.c \
srcdest_table.c \
spf_backoff.c \
+ libfrr.c \
strlcpy.c \
strlcat.c
@@ -52,10 +53,13 @@ pkginclude_HEADERS = \
event_counter.h \
monotime.h \
spf_backoff.h \
- srcdest_table.h
+ srcdest_table.h \
+ libfrr.h \
+ # end
noinst_HEADERS = \
- plist_int.h
+ plist_int.h \
+ log_int.h
noinst_PROGRAMS = grammar_sandbox
diff --git a/lib/command.c b/lib/command.c
index 50976f2010..bfff581d9b 100644
--- a/lib/command.c
+++ b/lib/command.c
@@ -30,6 +30,7 @@
#include "memory.h"
#include "log.h"
+#include "log_int.h"
#include <lib/version.h>
#include "thread.h"
#include "vector.h"
@@ -2163,7 +2164,7 @@ DEFUN (config_logmsg,
if ((level = level_match(argv[idx_log_level]->arg)) == ZLOG_DISABLED)
return CMD_ERR_NO_MATCH;
- zlog(NULL, level, "%s", ((message = argv_concat(argv, argc, idx_message)) ? message : ""));
+ zlog(level, "%s", ((message = argv_concat(argv, argc, idx_message)) ? message : ""));
if (message)
XFREE(MTYPE_TMP, message);
@@ -2214,7 +2215,7 @@ DEFUN (show_logging,
vty_out (vty, "%s", VTY_NEWLINE);
vty_out (vty, "Protocol name: %s%s",
- zlog_proto_names[zl->protocol], VTY_NEWLINE);
+ zl->protoname, VTY_NEWLINE);
vty_out (vty, "Record priority: %s%s",
(zl->record_priority ? "enabled" : "disabled"), VTY_NEWLINE);
vty_out (vty, "Timestamp precision: %d%s",
@@ -2234,14 +2235,14 @@ DEFUN (config_log_stdout,
if (argc == idx_log_level)
{
- zlog_set_level (NULL, ZLOG_DEST_STDOUT, zlog_default->default_lvl);
+ zlog_set_level (ZLOG_DEST_STDOUT, zlog_default->default_lvl);
return CMD_SUCCESS;
}
int level;
if ((level = level_match(argv[idx_log_level]->arg)) == ZLOG_DISABLED)
return CMD_ERR_NO_MATCH;
- zlog_set_level (NULL, ZLOG_DEST_STDOUT, level);
+ zlog_set_level (ZLOG_DEST_STDOUT, level);
return CMD_SUCCESS;
}
@@ -2253,7 +2254,7 @@ DEFUN (no_config_log_stdout,
"Cancel logging to stdout\n"
LOG_LEVEL_DESC)
{
- zlog_set_level (NULL, ZLOG_DEST_STDOUT, ZLOG_DISABLED);
+ zlog_set_level (ZLOG_DEST_STDOUT, ZLOG_DISABLED);
return CMD_SUCCESS;
}
@@ -2268,14 +2269,14 @@ DEFUN (config_log_monitor,
if (argc == idx_log_level)
{
- zlog_set_level (NULL, ZLOG_DEST_MONITOR, zlog_default->default_lvl);
+ zlog_set_level (ZLOG_DEST_MONITOR, zlog_default->default_lvl);
return CMD_SUCCESS;
}
int level;
if ((level = level_match(argv[idx_log_level]->arg)) == ZLOG_DISABLED)
return CMD_ERR_NO_MATCH;
- zlog_set_level (NULL, ZLOG_DEST_MONITOR, level);
+ zlog_set_level (ZLOG_DEST_MONITOR, level);
return CMD_SUCCESS;
}
@@ -2287,7 +2288,7 @@ DEFUN (no_config_log_monitor,
"Disable terminal line (monitor) logging\n"
LOG_LEVEL_DESC)
{
- zlog_set_level (NULL, ZLOG_DEST_MONITOR, ZLOG_DISABLED);
+ zlog_set_level (ZLOG_DEST_MONITOR, ZLOG_DISABLED);
return CMD_SUCCESS;
}
@@ -2322,7 +2323,7 @@ set_log_file(struct vty *vty, const char *fname, int loglevel)
else
fullpath = fname;
- ret = zlog_set_file (NULL, fullpath, loglevel);
+ ret = zlog_set_file (fullpath, loglevel);
if (p)
XFREE (MTYPE_TMP, p);
@@ -2376,7 +2377,7 @@ DEFUN (no_config_log_file,
"Logging file name\n"
"Logging level\n")
{
- zlog_reset_file (NULL);
+ zlog_reset_file ();
if (host.logfile)
XFREE (MTYPE_HOST, host.logfile);
@@ -2399,12 +2400,12 @@ DEFUN (config_log_syslog,
int level;
if ((level = level_match (argv[idx_log_levels]->arg)) == ZLOG_DISABLED)
return CMD_ERR_NO_MATCH;
- zlog_set_level (NULL, ZLOG_DEST_SYSLOG, level);
+ zlog_set_level (ZLOG_DEST_SYSLOG, level);
return CMD_SUCCESS;
}
else
{
- zlog_set_level (NULL, ZLOG_DEST_SYSLOG, zlog_default->default_lvl);
+ zlog_set_level (ZLOG_DEST_SYSLOG, zlog_default->default_lvl);
return CMD_SUCCESS;
}
}
@@ -2418,7 +2419,7 @@ DEFUN (no_config_log_syslog,
LOG_FACILITY_DESC
LOG_LEVEL_DESC)
{
- zlog_set_level (NULL, ZLOG_DEST_SYSLOG, ZLOG_DISABLED);
+ zlog_set_level (ZLOG_DEST_SYSLOG, ZLOG_DISABLED);
return CMD_SUCCESS;
}
@@ -2726,7 +2727,6 @@ cmd_init (int terminal)
vrf_install_commands ();
}
- srandom(time(NULL));
#ifdef DEV_BUILD
grammar_sandbox_init();
diff --git a/lib/filter.c b/lib/filter.c
index 2b9ba87137..fd73d4de73 100644
--- a/lib/filter.c
+++ b/lib/filter.c
@@ -1697,9 +1697,7 @@ filter_show (struct vty *vty, const char *name, afi_t afi)
return 0;
/* Print the name of the protocol */
- if (zlog_default)
- vty_out (vty, "%s:%s",
- zlog_proto_names[zlog_default->protocol], VTY_NEWLINE);
+ vty_out(vty, "%s:%s", zlog_protoname(), VTY_NEWLINE);
for (access = master->num.head; access; access = access->next)
{
diff --git a/lib/grammar_sandbox_main.c b/lib/grammar_sandbox_main.c
index 5deef406c1..681d4da440 100644
--- a/lib/grammar_sandbox_main.c
+++ b/lib/grammar_sandbox_main.c
@@ -40,11 +40,11 @@ int main(int argc, char **argv)
master = thread_master_create ();
- zlog_default = openzlog ("grammar_sandbox", ZLOG_NONE, 0,
+ openzlog ("grammar_sandbox", "NONE", 0,
LOG_CONS|LOG_NDELAY|LOG_PID, LOG_DAEMON);
- zlog_set_level (NULL, ZLOG_DEST_SYSLOG, ZLOG_DISABLED);
- zlog_set_level (NULL, ZLOG_DEST_STDOUT, LOG_DEBUG);
- zlog_set_level (NULL, ZLOG_DEST_MONITOR, ZLOG_DISABLED);
+ zlog_set_level (ZLOG_DEST_SYSLOG, ZLOG_DISABLED);
+ zlog_set_level (ZLOG_DEST_STDOUT, LOG_DEBUG);
+ zlog_set_level (ZLOG_DEST_MONITOR, ZLOG_DISABLED);
/* Library inits. */
cmd_init (1);
diff --git a/lib/if.c b/lib/if.c
index 6ee84e126c..f25be591d9 100644
--- a/lib/if.c
+++ b/lib/if.c
@@ -987,7 +987,7 @@ connected_log (struct connected *connected, char *str)
strncat (logbuf, inet_ntop (p->family, &p->u.prefix, buf, BUFSIZ),
BUFSIZ - strlen(logbuf));
}
- zlog (NULL, LOG_INFO, "%s", logbuf);
+ zlog_info("%s", logbuf);
}
/* Print if_addr structure. */
@@ -1007,7 +1007,7 @@ nbr_connected_log (struct nbr_connected *connected, char *str)
inet_ntop (p->family, &p->u.prefix, buf, BUFSIZ),
p->prefixlen);
- zlog (NULL, LOG_INFO, "%s", logbuf);
+ zlog_info("%s", logbuf);
}
/* If two connected address has same prefix return 1. */
diff --git a/lib/libfrr.c b/lib/libfrr.c
new file mode 100644
index 0000000000..f9ac3158a3
--- /dev/null
+++ b/lib/libfrr.c
@@ -0,0 +1,392 @@
+/*
+ * libfrr overall management functions
+ *
+ * Copyright (C) 2016 David Lamparter for NetDEF, 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; if not, write to the Free Software Foundation, Inc., 59 Temple
+ * Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <zebra.h>
+
+#include "libfrr.h"
+#include "getopt.h"
+#include "vty.h"
+#include "command.h"
+#include "version.h"
+#include "memory_vty.h"
+#include "zclient.h"
+
+const char frr_sysconfdir[] = SYSCONFDIR;
+const char frr_vtydir[] = DAEMON_VTY_DIR;
+
+char config_default[256];
+static char pidfile_default[256];
+static char vtypath_default[256];
+
+static char comb_optstr[256];
+static struct option comb_lo[64];
+static struct option *comb_next_lo = &comb_lo[0];
+static char comb_helpstr[4096];
+
+struct optspec {
+ const char *optstr;
+ const char *helpstr;
+ const struct option *longopts;
+};
+
+static void opt_extend(const struct optspec *os)
+{
+ const struct option *lo;
+
+ strcat(comb_optstr, os->optstr);
+ strcat(comb_helpstr, os->helpstr);
+ for (lo = os->longopts; lo->name; lo++)
+ memcpy(comb_next_lo++, lo, sizeof(*lo));
+}
+
+
+#define OPTION_VTYSOCK 1000
+
+static const struct option lo_always[] = {
+ { "help", no_argument, NULL, 'h' },
+ { "version", no_argument, NULL, 'v' },
+ { "daemon", no_argument, NULL, 'd' },
+ { "vty_socket", required_argument, NULL, OPTION_VTYSOCK },
+ { NULL }
+};
+static const struct optspec os_always = {
+ "hvdi:",
+ " -h, --help Display this help and exit\n"
+ " -v, --version Print program version\n"
+ " -d, --daemon Runs in daemon mode\n"
+ " --vty_socket Override vty socket path\n",
+ lo_always
+};
+
+
+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' },
+ { NULL }
+};
+static const struct optspec os_cfg_pid_dry = {
+ "f:i:C",
+ " -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",
+ lo_cfg_pid_dry
+};
+
+
+static const struct option lo_zclient[] = {
+ { "socket", required_argument, NULL, 'z' },
+ { NULL }
+};
+static const struct optspec os_zclient = {
+ "z:",
+ " -z, --socket Set path of zebra socket\n",
+ lo_zclient
+};
+
+
+static const struct option lo_vty[] = {
+ { "vty_addr", required_argument, NULL, 'A'},
+ { "vty_port", required_argument, NULL, 'P'},
+ { NULL }
+};
+static const struct optspec os_vty = {
+ "A:P:",
+ " -A, --vty_addr Set vty's bind address\n"
+ " -P, --vty_port Set vty's port number\n",
+ lo_vty
+};
+
+
+static const struct option lo_user[] = {
+ { "user", required_argument, NULL, 'u'},
+ { "group", required_argument, NULL, 'g'},
+ { NULL }
+};
+static const struct optspec os_user = {
+ "u:g:",
+ " -u, --user User to run as\n"
+ " -g, --group Group to run as\n",
+ lo_user
+};
+
+
+static struct frr_daemon_info *di = NULL;
+
+void frr_preinit(struct frr_daemon_info *daemon, int argc, char **argv)
+{
+ di = daemon;
+
+ /* basename(), opencoded. */
+ char *p = strrchr(argv[0], '/');
+ di->progname = p ? p + 1 : argv[0];
+
+ umask(0027);
+
+ opt_extend(&os_always);
+ if (!(di->flags & FRR_NO_CFG_PID_DRY))
+ opt_extend(&os_cfg_pid_dry);
+ if (!(di->flags & FRR_NO_PRIVSEP))
+ opt_extend(&os_user);
+ if (!(di->flags & FRR_NO_ZCLIENT))
+ opt_extend(&os_zclient);
+ if (!(di->flags & FRR_NO_TCPVTY))
+ opt_extend(&os_vty);
+
+ snprintf(config_default, sizeof(config_default), "%s/%s.conf",
+ frr_sysconfdir, di->name);
+ snprintf(pidfile_default, sizeof(pidfile_default), "%s/%s.pid",
+ frr_vtydir, di->name);
+}
+
+void frr_opt_add(const char *optstr, const struct option *longopts,
+ const char *helpstr)
+{
+ const struct optspec main_opts = { optstr, helpstr, longopts };
+ opt_extend(&main_opts);
+}
+
+void frr_help_exit(int status)
+{
+ FILE *target = status ? stderr : stdout;
+
+ if (status != 0)
+ fprintf(stderr, "Invalid options.\n\n");
+
+ if (di->printhelp)
+ di->printhelp(target);
+ else
+ fprintf(target, "Usage: %s [OPTION...]\n\n%s%s%s\n\n%s",
+ di->progname,
+ di->proghelp,
+ di->copyright ? "\n\n" : "",
+ di->copyright ? di->copyright : "",
+ comb_helpstr);
+ fprintf(target, "\nReport bugs to %s\n", FRR_BUG_ADDRESS);
+ exit(status);
+}
+
+static int errors = 0;
+
+static int frr_opt(int opt)
+{
+ static int vty_port_set = 0;
+ static int vty_addr_set = 0;
+ char *err;
+
+ switch (opt) {
+ case 'h':
+ frr_help_exit(0);
+ break;
+ case 'v':
+ print_version(di->progname);
+ exit(0);
+ break;
+ case 'd':
+ di->daemon_mode = 1;
+ break;
+ case 'i':
+ if (di->flags & FRR_NO_CFG_PID_DRY)
+ return 1;
+ di->pid_file = optarg;
+ break;
+ case 'f':
+ if (di->flags & FRR_NO_CFG_PID_DRY)
+ return 1;
+ di->config_file = optarg;
+ break;
+ case 'C':
+ if (di->flags & FRR_NO_CFG_PID_DRY)
+ return 1;
+ di->dryrun = 1;
+ break;
+ case 'z':
+ if (di->flags & FRR_NO_ZCLIENT)
+ return 1;
+ zclient_serv_path_set(optarg);
+ break;
+ case 'A':
+ if (di->flags & FRR_NO_TCPVTY)
+ return 1;
+ if (vty_addr_set) {
+ fprintf(stderr, "-A option specified more than once!\n");
+ errors++;
+ break;
+ }
+ vty_addr_set = 1;
+ di->vty_addr = optarg;
+ break;
+ case 'P':
+ if (di->flags & FRR_NO_TCPVTY)
+ return 1;
+ if (vty_port_set) {
+ fprintf(stderr, "-P option specified more than once!\n");
+ errors++;
+ break;
+ }
+ vty_port_set = 1;
+ di->vty_port = strtoul(optarg, &err, 0);
+ if (*err || !*optarg) {
+ fprintf(stderr, "invalid port number \"%s\" for -P option\n",
+ optarg);
+ errors++;
+ break;
+ }
+ break;
+ case OPTION_VTYSOCK:
+ if (di->vty_sock_path) {
+ fprintf(stderr, "--vty_socket option specified more than once!\n");
+ errors++;
+ break;
+ }
+ di->vty_sock_path = optarg;
+ break;
+ case 'u':
+ if (di->flags & FRR_NO_PRIVSEP)
+ return 1;
+ di->privs->user = optarg;
+ break;
+ case 'g':
+ if (di->flags & FRR_NO_PRIVSEP)
+ return 1;
+ di->privs->group = optarg;
+ break;
+ default:
+ return 1;
+ }
+ return 0;
+}
+
+int frr_getopt(int argc, char * const argv[], int *longindex)
+{
+ int opt;
+ int lidx;
+
+ comb_next_lo->name = NULL;
+
+ do {
+ opt = getopt_long(argc, argv, comb_optstr, comb_lo, &lidx);
+ if (frr_opt(opt))
+ break;
+ } while (opt != -1);
+
+ if (opt == -1 && errors)
+ frr_help_exit(1);
+ if (longindex)
+ *longindex = lidx;
+ return opt;
+}
+
+struct thread_master *frr_init(void)
+{
+ struct thread_master *master;
+
+ srandom(time(NULL));
+
+ openzlog (di->progname, di->logname, di->instance,
+ LOG_CONS|LOG_NDELAY|LOG_PID, LOG_DAEMON);
+#if defined(HAVE_CUMULUS)
+ zlog_set_level (NULL, ZLOG_DEST_SYSLOG, zlog_default->default_lvl);
+#endif
+
+ zprivs_init(di->privs);
+
+ master = thread_master_create();
+ signal_init(master, di->n_signals, di->signals);
+
+ if (di->flags & FRR_LIMITED_CLI)
+ cmd_init(-1);
+ else
+ cmd_init(1);
+ vty_init(master);
+ memory_init();
+
+ return master;
+}
+
+void frr_config_fork(void)
+{
+ if (di->instance) {
+ snprintf(config_default, sizeof(config_default), "%s/%s-%d.conf",
+ frr_sysconfdir, di->name, di->instance);
+ snprintf(pidfile_default, sizeof(pidfile_default), "%s/%s-%d.pid",
+ frr_vtydir, di->name, di->instance);
+ }
+
+ vty_read_config(di->config_file, config_default);
+
+ /* Don't start execution if we are in dry-run mode */
+ 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->pid_file)
+ di->pid_file = pidfile_default;
+ pid_output (di->pid_file);
+}
+
+void frr_vty_serv(void)
+{
+ /* allow explicit override of vty_path in the future
+ * (not currently set anywhere) */
+ if (!di->vty_path) {
+ const char *dir;
+ dir = di->vty_sock_path ? di->vty_sock_path : frr_vtydir;
+
+ if (di->instance)
+ snprintf(vtypath_default, sizeof(vtypath_default),
+ "%s/%s-%d.vty",
+ dir, di->name, di->instance);
+ else
+ snprintf(vtypath_default, sizeof(vtypath_default),
+ "%s/%s.vty", dir, di->name);
+
+ di->vty_path = vtypath_default;
+ }
+
+ vty_serv_sock(di->vty_addr, di->vty_port, di->vty_path);
+}
+
+void frr_run(struct thread_master *master)
+{
+ char instanceinfo[64] = "";
+
+ frr_vty_serv();
+
+ if (di->instance)
+ snprintf(instanceinfo, sizeof(instanceinfo), "instance %u ",
+ di->instance);
+
+ zlog_notice("%s %s starting: %svty@%d%s",
+ di->name,
+ FRR_VERSION,
+ instanceinfo,
+ di->vty_port,
+ di->startinfo);
+
+ struct thread thread;
+ while (thread_fetch(master, &thread))
+ thread_call(&thread);
+}
diff --git a/lib/libfrr.h b/lib/libfrr.h
new file mode 100644
index 0000000000..d37f406f5b
--- /dev/null
+++ b/lib/libfrr.h
@@ -0,0 +1,100 @@
+/*
+ * libfrr overall management functions
+ *
+ * Copyright (C) 2016 David Lamparter for NetDEF, 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; if not, write to the Free Software Foundation, Inc., 59 Temple
+ * Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _ZEBRA_FRR_H
+#define _ZEBRA_FRR_H
+
+#include "sigevent.h"
+#include "privs.h"
+#include "thread.h"
+#include "log.h"
+#include "getopt.h"
+
+#define FRR_NO_PRIVSEP (1 << 0)
+#define FRR_NO_TCPVTY (1 << 1)
+#define FRR_LIMITED_CLI (1 << 2)
+#define FRR_NO_CFG_PID_DRY (1 << 3)
+#define FRR_NO_ZCLIENT (1 << 4)
+
+struct frr_daemon_info {
+ unsigned flags;
+
+ const char *progname;
+ const char *name;
+ const char *logname;
+ unsigned short instance;
+
+ char *vty_addr;
+ int vty_port;
+ char *vty_sock_path;
+ bool dryrun;
+ bool daemon_mode;
+ const char *config_file;
+ const char *pid_file;
+ const char *vty_path;
+
+ const char *proghelp;
+ void (*printhelp)(FILE *target);
+ const char *copyright;
+ char startinfo[128];
+
+ struct quagga_signal_t *signals;
+ size_t n_signals;
+
+ struct zebra_privs_t *privs;
+};
+
+/* execname is the daemon's executable (and pidfile and configfile) name,
+ * i.e. "zebra" or "bgpd"
+ * constname is the daemons source-level name, primarily for the logging ID,
+ * i.e. "ZEBRA" or "BGP"
+ *
+ * note that this macro is also a latch-on point for other changes (e.g.
+ * upcoming plugin support) that need to place some per-daemon things. Each
+ * daemon should have one of these.
+ */
+#define FRR_DAEMON_INFO(execname, constname, ...) \
+ static struct frr_daemon_info execname ##_di = { \
+ .name = # execname, \
+ .logname = # constname, \
+ __VA_ARGS__ \
+ };
+
+extern void frr_preinit(struct frr_daemon_info *daemon,
+ int argc, char **argv);
+extern void frr_opt_add(const char *optstr,
+ const struct option *longopts, const char *helpstr);
+extern int frr_getopt(int argc, char * const argv[], int *longindex);
+extern void frr_help_exit(int status);
+
+extern struct thread_master *frr_init(void);
+
+extern void frr_config_fork(void);
+
+extern void frr_vty_serv(void);
+
+/* note: contains call to frr_vty_serv() */
+extern void frr_run(struct thread_master *master);
+
+extern char config_default[256];
+extern const char frr_sysconfdir[];
+extern const char frr_vtydir[];
+
+#endif /* _ZEBRA_FRR_H */
diff --git a/lib/log.c b/lib/log.c
index f400290435..0fd9621f37 100644
--- a/lib/log.c
+++ b/lib/log.c
@@ -26,6 +26,7 @@
#include "zclient.h"
#include "log.h"
+#include "log_int.h"
#include "memory.h"
#include "command.h"
#ifndef SUNOS_5
@@ -42,28 +43,6 @@ static int logfile_fd = -1; /* Used in signal handler. */
struct zlog *zlog_default = NULL;
-/*
- * This must be kept in the same order as the
- * zlog_proto_t enum
- */
-const char *zlog_proto_names[] =
-{
- "NONE",
- "DEFAULT",
- "ZEBRA",
- "RIP",
- "BGP",
- "OSPF",
- "RIPNG",
- "OSPF6",
- "LDP",
- "ISIS",
- "PIM",
- "RFP",
- "WATCHFRR",
- NULL,
-};
-
const char *zlog_priority[] =
{
"emergencies",
@@ -184,16 +163,13 @@ time_print(FILE *fp, struct timestamp_control *ctl)
/* va_list version of zlog. */
void
-vzlog (struct zlog *zl, int priority, const char *format, va_list args)
+vzlog (int priority, const char *format, va_list args)
{
char proto_str[32];
int original_errno = errno;
struct timestamp_control tsctl;
tsctl.already_rendered = 0;
-
- /* If zlog is not specified, use default one. */
- if (zl == NULL)
- zl = zlog_default;
+ struct zlog *zl = zlog_default;
/* When zlog_default is also NULL, use stderr for logging. */
if (zl == NULL)
@@ -221,9 +197,9 @@ vzlog (struct zlog *zl, int priority, const char *format, va_list args)
}
if (zl->instance)
- sprintf (proto_str, "%s[%d]: ", zlog_proto_names[zl->protocol], zl->instance);
+ sprintf (proto_str, "%s[%d]: ", zl->protoname, zl->instance);
else
- sprintf (proto_str, "%s: ", zlog_proto_names[zl->protocol]);
+ sprintf (proto_str, "%s: ", zl->protoname);
/* File output. */
if ((priority <= zl->maxlvl[ZLOG_DEST_FILE]) && zl->fp)
@@ -264,11 +240,9 @@ vzlog (struct zlog *zl, int priority, const char *format, va_list args)
}
int
-vzlog_test (struct zlog *zl, int priority)
+vzlog_test (int priority)
{
- /* If zlog is not specified, use default one. */
- if (zl == NULL)
- zl = zlog_default;
+ struct zlog *zl = zlog_default;
/* When zlog_default is also NULL, use stderr for logging. */
if (zl == NULL)
@@ -455,7 +429,7 @@ zlog_signal(int signo, const char *action
time(&now);
if (zlog_default)
{
- s = str_append(LOC,zlog_proto_names[zlog_default->protocol]);
+ s = str_append(LOC,zlog_default->protoname);
*s++ = ':';
*s++ = ' ';
msgstart = s;
@@ -638,7 +612,7 @@ void
zlog_backtrace(int priority)
{
#ifndef HAVE_GLIBC_BACKTRACE
- zlog(NULL, priority, "No backtrace available on this platform.");
+ zlog(priority, "No backtrace available on this platform.");
#else
void *array[20];
int size, i;
@@ -652,29 +626,29 @@ zlog_backtrace(int priority)
size, (unsigned long)(array_size(array)));
return;
}
- zlog(NULL, priority, "Backtrace for %d stack frames:", size);
+ zlog(priority, "Backtrace for %d stack frames:", size);
if (!(strings = backtrace_symbols(array, size)))
{
zlog_err("Cannot get backtrace symbols (out of memory?)");
for (i = 0; i < size; i++)
- zlog(NULL, priority, "[bt %d] %p",i,array[i]);
+ zlog(priority, "[bt %d] %p",i,array[i]);
}
else
{
for (i = 0; i < size; i++)
- zlog(NULL, priority, "[bt %d] %s",i,strings[i]);
+ zlog(priority, "[bt %d] %s",i,strings[i]);
free(strings);
}
#endif /* HAVE_GLIBC_BACKTRACE */
}
void
-zlog (struct zlog *zl, int priority, const char *format, ...)
+zlog (int priority, const char *format, ...)
{
va_list args;
va_start(args, format);
- vzlog (zl, priority, format, args);
+ vzlog (priority, format, args);
va_end (args);
}
@@ -684,7 +658,7 @@ FUNCNAME(const char *format, ...) \
{ \
va_list args; \
va_start(args, format); \
- vzlog (NULL, PRIORITY, format, args); \
+ vzlog (PRIORITY, format, args); \
va_end(args); \
}
@@ -703,11 +677,11 @@ ZLOG_FUNC(zlog_debug, LOG_DEBUG)
void zlog_thread_info (int log_level)
{
if (thread_current)
- zlog(NULL, log_level, "Current thread function %s, scheduled from "
+ zlog(log_level, "Current thread function %s, scheduled from "
"file %s, line %u", thread_current->funcname,
thread_current->schedfrom, thread_current->schedfrom_line);
else
- zlog(NULL, log_level, "Current thread not known/applicable");
+ zlog(log_level, "Current thread not known/applicable");
}
void
@@ -719,7 +693,7 @@ _zlog_assert_failed (const char *assertion, const char *file,
((logfile_fd = open_crashlog()) >= 0) &&
((zlog_default->fp = fdopen(logfile_fd, "w")) != NULL))
zlog_default->maxlvl[ZLOG_DEST_FILE] = LOG_ERR;
- zlog(NULL, LOG_CRIT, "Assertion `%s' failed in file %s, line %u, function %s",
+ zlog(LOG_CRIT, "Assertion `%s' failed in file %s, line %u, function %s",
assertion,file,line,(function ? function : "?"));
zlog_backtrace(LOG_CRIT);
zlog_thread_info(LOG_CRIT);
@@ -737,8 +711,8 @@ memory_oom (size_t size, const char *name)
}
/* Open log stream */
-struct zlog *
-openzlog (const char *progname, zlog_proto_t protocol, u_short instance,
+void
+openzlog (const char *progname, const char *protoname, u_short instance,
int syslog_flags, int syslog_facility)
{
struct zlog *zl;
@@ -747,7 +721,7 @@ openzlog (const char *progname, zlog_proto_t protocol, u_short instance,
zl = XCALLOC(MTYPE_ZLOG, sizeof (struct zlog));
zl->ident = progname;
- zl->protocol = protocol;
+ zl->protoname = protoname;
zl->instance = instance;
zl->facility = syslog_facility;
zl->syslog_options = syslog_flags;
@@ -759,13 +733,14 @@ openzlog (const char *progname, zlog_proto_t protocol, u_short instance,
zl->default_lvl = LOG_DEBUG;
openlog (progname, syslog_flags, zl->facility);
-
- return zl;
+ zlog_default = zl;
}
void
-closezlog (struct zlog *zl)
+closezlog (void)
{
+ struct zlog *zl = zlog_default;
+
closelog();
if (zl->fp != NULL)
@@ -775,30 +750,31 @@ closezlog (struct zlog *zl)
XFREE(MTYPE_ZLOG, zl->filename);
XFREE (MTYPE_ZLOG, zl);
+ zlog_default = NULL;
+}
+
+const char *
+zlog_protoname (void)
+{
+ return zlog_default ? zlog_default->protoname : "NONE";
}
/* Called from command.c. */
void
-zlog_set_level (struct zlog *zl, zlog_dest_t dest, int log_level)
+zlog_set_level (zlog_dest_t dest, int log_level)
{
- if (zl == NULL)
- zl = zlog_default;
-
- zl->maxlvl[dest] = log_level;
+ zlog_default->maxlvl[dest] = log_level;
}
int
-zlog_set_file (struct zlog *zl, const char *filename, int log_level)
+zlog_set_file (const char *filename, int log_level)
{
+ struct zlog *zl = zlog_default;
FILE *fp;
mode_t oldumask;
/* There is opend file. */
- zlog_reset_file (zl);
-
- /* Set default zl. */
- if (zl == NULL)
- zl = zlog_default;
+ zlog_reset_file ();
/* Open file. */
oldumask = umask (0777 & ~LOGFILE_MASK);
@@ -818,10 +794,9 @@ zlog_set_file (struct zlog *zl, const char *filename, int log_level)
/* Reset opend file. */
int
-zlog_reset_file (struct zlog *zl)
+zlog_reset_file (void)
{
- if (zl == NULL)
- zl = zlog_default;
+ struct zlog *zl = zlog_default;
if (zl->fp)
fclose (zl->fp);
@@ -838,13 +813,11 @@ zlog_reset_file (struct zlog *zl)
/* Reopen log file. */
int
-zlog_rotate (struct zlog *zl)
+zlog_rotate (void)
{
+ struct zlog *zl = zlog_default;
int level;
- if (zl == NULL)
- zl = zlog_default;
-
if (zl->fp)
fclose (zl->fp);
zl->fp = NULL;
@@ -1083,6 +1056,8 @@ proto_redistnum(int afi, const char *s)
return ZEBRA_ROUTE_VNC;
else if (strmatch (s, "vnc-direct"))
return ZEBRA_ROUTE_VNC_DIRECT;
+ else if (strncmp (s, "n", 1) == 0)
+ return ZEBRA_ROUTE_NHRP;
}
if (afi == AFI_IP6)
{
@@ -1106,6 +1081,8 @@ proto_redistnum(int afi, const char *s)
return ZEBRA_ROUTE_VNC;
else if (strmatch (s, "vnc-direct"))
return ZEBRA_ROUTE_VNC_DIRECT;
+ else if (strncmp (s, "n", 1) == 0)
+ return ZEBRA_ROUTE_NHRP;
}
return -1;
}
diff --git a/lib/log.h b/lib/log.h
index 43fc0130db..8a65bbbf51 100644
--- a/lib/log.h
+++ b/lib/log.h
@@ -24,6 +24,7 @@
#define _ZEBRA_LOG_H
#include <syslog.h>
+#include <stdio.h>
/* Here is some guidance on logging levels to use:
*
@@ -41,27 +42,6 @@
* please use LOG_ERR instead.
*/
-/*
- * This must be kept in the same order as
- * zlog_proto_names[]
- */
-typedef enum
-{
- ZLOG_NONE,
- ZLOG_DEFAULT,
- ZLOG_ZEBRA,
- ZLOG_RIP,
- ZLOG_BGP,
- ZLOG_OSPF,
- ZLOG_RIPNG,
- ZLOG_OSPF6,
- ZLOG_LDP,
- ZLOG_ISIS,
- ZLOG_PIM,
- ZLOG_RFP,
- ZLOG_WATCHFRR,
-} zlog_proto_t;
-
/* If maxlvl is set to ZLOG_DISABLED, then no messages will be sent
to that logging destination. */
#define ZLOG_DISABLED (LOG_EMERG-1)
@@ -75,23 +55,6 @@ typedef enum
} zlog_dest_t;
#define ZLOG_NUM_DESTS (ZLOG_DEST_FILE+1)
-struct zlog
-{
- const char *ident; /* daemon name (first arg to openlog) */
- zlog_proto_t protocol;
- u_short instance;
- int maxlvl[ZLOG_NUM_DESTS]; /* maximum priority to send to associated
- logging destination */
- int default_lvl; /* maxlvl to use if none is specified */
- FILE *fp;
- char *filename;
- int facility; /* as per syslog facility */
- int record_priority; /* should messages logged through stdio include the
- priority of the message? */
- int syslog_options; /* 2nd arg to openlog */
- int timestamp_precision; /* # of digits of subsecond precision */
-};
-
/* Message structure. */
struct message
{
@@ -99,15 +62,14 @@ struct message
const char *str;
};
-/* Default logging strucutre. */
-extern struct zlog *zlog_default;
-
/* Open zlog function */
-extern struct zlog *openzlog (const char *progname, zlog_proto_t protocol,
- u_short instance, int syslog_options, int syslog_facility);
+extern void openzlog (const char *progname, const char *protoname,
+ u_short instance, int syslog_options, int syslog_facility);
/* Close zlog function. */
-extern void closezlog (struct zlog *zl);
+extern void closezlog (void);
+
+extern const char *zlog_protoname (void);
/* GCC have printf type attribute check. */
#ifdef __GNUC__
@@ -116,35 +78,28 @@ extern void closezlog (struct zlog *zl);
#define PRINTF_ATTRIBUTE(a,b)
#endif /* __GNUC__ */
-/* Generic function for zlog. */
-extern void zlog (struct zlog *zl, int priority, const char *format, ...)
- PRINTF_ATTRIBUTE(3, 4);
-
/* Handy zlog functions. */
-extern void vzlog (struct zlog *zl, int priority, const char *format, va_list args);
extern void zlog_err (const char *format, ...) PRINTF_ATTRIBUTE(1, 2);
extern void zlog_warn (const char *format, ...) PRINTF_ATTRIBUTE(1, 2);
extern void zlog_info (const char *format, ...) PRINTF_ATTRIBUTE(1, 2);
extern void zlog_notice (const char *format, ...) PRINTF_ATTRIBUTE(1, 2);
extern void zlog_debug (const char *format, ...) PRINTF_ATTRIBUTE(1, 2);
-extern void vzlog (struct zlog *, int , const char *, va_list );
-
extern void zlog_thread_info (int log_level);
/* Set logging level for the given destination. If the log_level
argument is ZLOG_DISABLED, then the destination is disabled.
This function should not be used for file logging (use zlog_set_file
or zlog_reset_file instead). */
-extern void zlog_set_level (struct zlog *zl, zlog_dest_t, int log_level);
+extern void zlog_set_level (zlog_dest_t, int log_level);
/* Set logging to the given filename at the specified level. */
-extern int zlog_set_file (struct zlog *zl, const char *filename, int log_level);
+extern int zlog_set_file (const char *filename, int log_level);
/* Disable file logging. */
-extern int zlog_reset_file (struct zlog *zl);
+extern int zlog_reset_file (void);
/* Rotate log. */
-extern int zlog_rotate (struct zlog *);
+extern int zlog_rotate (void);
/* For hackey message lookup and check */
#define LOOKUP_DEF(x, y, def) mes_lookup(x, x ## _max, y, def, #x)
@@ -155,9 +110,6 @@ extern const char *mes_lookup (const struct message *meslist,
int max, int index,
const char *no_item, const char *mesname);
-extern const char *zlog_priority[];
-extern const char *zlog_proto_names[];
-
/* Safe version of strerror -- never returns NULL. */
extern const char *safe_strerror(int errnum);
@@ -191,8 +143,7 @@ extern void zlog_hexdump(const void *mem, unsigned int len);
extern const char *zlog_sanitize(char *buf, size_t bufsz, const void *in, size_t inlen);
-extern int
-vzlog_test (struct zlog *zl, int priority);
+extern int vzlog_test (int priority);
/* structure useful for avoiding repeated rendering of the same timestamp */
struct timestamp_control {
diff --git a/lib/log_int.h b/lib/log_int.h
new file mode 100644
index 0000000000..c21d723ac6
--- /dev/null
+++ b/lib/log_int.h
@@ -0,0 +1,57 @@
+/*
+ * Zebra logging funcions.
+ * Copyright (C) 1997, 1998, 1999 Kunihiro Ishiguro
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * GNU Zebra is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GNU Zebra; see the file COPYING. If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#ifndef _ZEBRA_LOG_PRIVATE_H
+#define _ZEBRA_LOG_PRIVATE_H
+
+#include "log.h"
+
+struct zlog
+{
+ const char *ident; /* daemon name (first arg to openlog) */
+ const char *protoname;
+ u_short instance;
+ int maxlvl[ZLOG_NUM_DESTS]; /* maximum priority to send to associated
+ logging destination */
+ int default_lvl; /* maxlvl to use if none is specified */
+ FILE *fp;
+ char *filename;
+ int facility; /* as per syslog facility */
+ int record_priority; /* should messages logged through stdio include the
+ priority of the message? */
+ int syslog_options; /* 2nd arg to openlog */
+ int timestamp_precision; /* # of digits of subsecond precision */
+};
+
+/* Default logging strucutre. */
+extern struct zlog *zlog_default;
+
+extern const char *zlog_priority[];
+
+/* Generic function for zlog. */
+extern void vzlog (int priority, const char *format, va_list args);
+extern void zlog (int priority, const char *format, ...)
+ PRINTF_ATTRIBUTE(2, 3);
+
+#endif /* _ZEBRA_LOG_PRIVATE_H */
+
+
diff --git a/lib/plist.c b/lib/plist.c
index 3ed5c8fc5c..3714969696 100644
--- a/lib/plist.c
+++ b/lib/plist.c
@@ -1175,8 +1175,7 @@ vty_show_prefix_entry (struct vty *vty, afi_t afi, struct prefix_list *plist,
struct prefix_list_entry *pentry;
/* Print the name of the protocol */
- if (zlog_default)
- vty_out (vty, "%s: ", zlog_proto_names[zlog_default->protocol]);
+ vty_out(vty, "%s: ", zlog_protoname());
if (dtype == normal_display)
{
diff --git a/lib/prefix.c b/lib/prefix.c
index dec22a44a3..0cc759bb7c 100644
--- a/lib/prefix.c
+++ b/lib/prefix.c
@@ -313,8 +313,7 @@ prefix_copy (struct prefix *dest, const struct prefix *src)
}
else
{
- zlog (NULL, LOG_ERR, "prefix_copy(): Unknown address family %d",
- src->family);
+ zlog_err("prefix_copy(): Unknown address family %d", src->family);
assert (0);
}
}
diff --git a/lib/route_types.txt b/lib/route_types.txt
index 74d4f2a49c..5cb06ffb7f 100644
--- a/lib/route_types.txt
+++ b/lib/route_types.txt
@@ -52,6 +52,7 @@ ZEBRA_ROUTE_OSPF6, ospf6, ospf6d, 'O', 0, 1, "OSPFv3"
ZEBRA_ROUTE_ISIS, isis, isisd, 'I', 1, 1, "IS-IS"
ZEBRA_ROUTE_BGP, bgp, bgpd, 'B', 1, 1, "BGP"
ZEBRA_ROUTE_PIM, pim, pimd, 'P', 1, 0, "PIM"
+ZEBRA_ROUTE_NHRP, nhrp, nhrpd, 'N', 1, 1, "NHRP"
# HSLS and OLSR both are AFI independent (so: 1, 1), however
# we want to disable for them for general Quagga distribution.
# This at least makes it trivial for users of these protocols
@@ -85,6 +86,7 @@ ZEBRA_ROUTE_OSPF6, "Open Shortest Path First (IPv6) (OSPFv3)"
ZEBRA_ROUTE_ISIS, "Intermediate System to Intermediate System (IS-IS)"
ZEBRA_ROUTE_BGP, "Border Gateway Protocol (BGP)"
ZEBRA_ROUTE_PIM, "Protocol Independent Multicast (PIM)"
+ZEBRA_ROUTE_NHRP, "Next Hop Resolution Protocol (NHRP)"
ZEBRA_ROUTE_HSLS, "Hazy-Sighted Link State Protocol (HSLS)"
ZEBRA_ROUTE_VNC, "Virtual Network Control (VNC)"
ZEBRA_ROUTE_OLSR, "Optimised Link State Routing (OLSR)"
diff --git a/lib/routemap.c b/lib/routemap.c
index 39d9a5d375..1647ac3668 100644
--- a/lib/routemap.c
+++ b/lib/routemap.c
@@ -28,6 +28,7 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
#include "routemap.h"
#include "command.h"
#include "log.h"
+#include "log_int.h"
#include "hash.h"
DEFINE_MTYPE_STATIC(LIB, ROUTE_MAP, "Route map")
@@ -991,7 +992,7 @@ vty_show_route_map_entry (struct vty *vty, struct route_map *map)
/* Print the name of the protocol */
if (zlog_default)
{
- vty_out (vty, "%s", zlog_proto_names[zlog_default->protocol]);
+ vty_out (vty, "%s", zlog_protoname());
if (zlog_default->instance)
vty_out (vty, " %d", zlog_default->instance);
}
@@ -1051,8 +1052,7 @@ vty_show_route_map (struct vty *vty, const char *name)
}
else
{
- if (zlog_default)
- vty_out (vty, "%s", zlog_proto_names[zlog_default->protocol]);
+ vty_out (vty, "%s", zlog_protoname());
if (zlog_default && zlog_default->instance)
vty_out (vty, " %d", zlog_default->instance);
vty_out (vty, ": 'route-map %s' not found%s", name, VTY_NEWLINE);
@@ -1614,9 +1614,8 @@ route_map_apply (struct route_map *map, struct prefix *prefix,
if (recursion > RMAP_RECURSION_LIMIT)
{
- zlog (NULL, LOG_WARNING,
- "route-map recursion limit (%d) reached, discarding route",
- RMAP_RECURSION_LIMIT);
+ zlog_warn("route-map recursion limit (%d) reached, discarding route",
+ RMAP_RECURSION_LIMIT);
recursion = 0;
return RMAP_DENYMATCH;
}
diff --git a/lib/sockopt.c b/lib/sockopt.c
index 91b0602b3a..2a9f907cb3 100644
--- a/lib/sockopt.c
+++ b/lib/sockopt.c
@@ -29,29 +29,6 @@
#include "sockopt.h"
#include "sockunion.h"
-/* Replace the path of given defaultpath with newpath, but keep filename */
-void
-set_socket_path (char *path, const char *defaultpath, char *newpath, int maxsize)
-{
- const char *sock_name;
-
- sock_name = strrchr(defaultpath, '/');
- if (sock_name)
- /* skip '/' */
- sock_name++;
- else
- /*
- * VTYSH_PATH configured as relative path
- * during config? Should really never happen for
- * sensible config
- */
- sock_name = defaultpath;
-
- strlcpy (path, newpath, maxsize);
- strlcat (path, "/", maxsize);
- strlcat (path, sock_name, maxsize);
-}
-
void
setsockopt_so_recvbuf (int sock, int size)
{
diff --git a/lib/sockopt.h b/lib/sockopt.h
index d5724ce60f..1b7be1e49f 100644
--- a/lib/sockopt.h
+++ b/lib/sockopt.h
@@ -24,10 +24,6 @@
#include "sockunion.h"
-/* Override (vty) socket paths, but keep the filename */
-extern void set_socket_path (char *path, const char *defaultpath,
- char *newpath, int maxsize);
-
extern void setsockopt_so_recvbuf (int sock, int size);
extern void setsockopt_so_sendbuf (const int sock, int size);
extern int getsockopt_so_sendbuf (const int sock);
diff --git a/lib/sockunion.c b/lib/sockunion.c
index 5b508d1bf8..9ba2ce82f6 100644
--- a/lib/sockunion.c
+++ b/lib/sockunion.c
@@ -148,8 +148,8 @@ sockunion_socket (const union sockunion *su)
if (sock < 0)
{
char buf[SU_ADDRSTRLEN];
- zlog (NULL, LOG_WARNING, "Can't make socket for %s : %s",
- sockunion_log (su, buf, SU_ADDRSTRLEN), safe_strerror (errno));
+ zlog_warn("Can't make socket for %s : %s",
+ sockunion_log(su, buf, SU_ADDRSTRLEN), safe_strerror(errno));
return -1;
}
@@ -264,7 +264,7 @@ sockunion_stream_socket (union sockunion *su)
sock = socket (su->sa.sa_family, SOCK_STREAM, 0);
if (sock < 0)
- zlog (NULL, LOG_WARNING, "can't make socket sockunion_stream_socket");
+ zlog_warn("can't make socket sockunion_stream_socket");
return sock;
}
@@ -308,8 +308,8 @@ sockunion_bind (int sock, union sockunion *su, unsigned short port,
if (ret < 0)
{
char buf[SU_ADDRSTRLEN];
- zlog (NULL, LOG_WARNING, "can't bind socket for %s : %s",
- sockunion_log (su, buf, SU_ADDRSTRLEN), safe_strerror (errno));
+ zlog_warn("can't bind socket for %s : %s",
+ sockunion_log(su, buf, SU_ADDRSTRLEN), safe_strerror(errno));
}
return ret;
@@ -325,7 +325,7 @@ sockopt_reuseaddr (int sock)
(void *) &on, sizeof (on));
if (ret < 0)
{
- zlog (NULL, LOG_WARNING, "can't set sockopt SO_REUSEADDR to socket %d", sock);
+ zlog_warn("can't set sockopt SO_REUSEADDR to socket %d", sock);
return -1;
}
return 0;
@@ -342,7 +342,7 @@ sockopt_reuseport (int sock)
(void *) &on, sizeof (on));
if (ret < 0)
{
- zlog (NULL, LOG_WARNING, "can't set sockopt SO_REUSEPORT to socket %d", sock);
+ zlog_warn("can't set sockopt SO_REUSEPORT to socket %d", sock);
return -1;
}
return 0;
@@ -367,7 +367,7 @@ sockopt_ttl (int family, int sock, int ttl)
(void *) &ttl, sizeof (int));
if (ret < 0)
{
- zlog (NULL, LOG_WARNING, "can't set sockopt IP_TTL %d to socket %d", ttl, sock);
+ zlog_warn("can't set sockopt IP_TTL %d to socket %d", ttl, sock);
return -1;
}
return 0;
@@ -379,8 +379,8 @@ sockopt_ttl (int family, int sock, int ttl)
(void *) &ttl, sizeof (int));
if (ret < 0)
{
- zlog (NULL, LOG_WARNING, "can't set sockopt IPV6_UNICAST_HOPS %d to socket %d",
- ttl, sock);
+ zlog_warn("can't set sockopt IPV6_UNICAST_HOPS %d to socket %d",
+ ttl, sock);
return -1;
}
return 0;
@@ -425,9 +425,8 @@ sockopt_minttl (int family, int sock, int minttl)
{
int ret = setsockopt (sock, IPPROTO_IP, IP_MINTTL, &minttl, sizeof(minttl));
if (ret < 0)
- zlog (NULL, LOG_WARNING,
- "can't set sockopt IP_MINTTL to %d on socket %d: %s",
- minttl, sock, safe_strerror (errno));
+ zlog_warn("can't set sockopt IP_MINTTL to %d on socket %d: %s",
+ minttl, sock, safe_strerror(errno));
return ret;
}
#endif /* IP_MINTTL */
@@ -436,9 +435,8 @@ sockopt_minttl (int family, int sock, int minttl)
{
int ret = setsockopt (sock, IPPROTO_IPV6, IPV6_MINHOPCOUNT, &minttl, sizeof(minttl));
if (ret < 0)
- zlog (NULL, LOG_WARNING,
- "can't set sockopt IPV6_MINHOPCOUNT to %d on socket %d: %s",
- minttl, sock, safe_strerror (errno));
+ zlog_warn("can't set sockopt IPV6_MINHOPCOUNT to %d on socket %d: %s",
+ minttl, sock, safe_strerror(errno));
return ret;
}
#endif
@@ -459,8 +457,8 @@ sockopt_v6only (int family, int sock)
(void *) &on, sizeof (int));
if (ret < 0)
{
- zlog (NULL, LOG_WARNING, "can't set sockopt IPV6_V6ONLY "
- "to socket %d", sock);
+ zlog_warn("can't set sockopt IPV6_V6ONLY " "to socket %d",
+ sock);
return -1;
}
return 0;
@@ -626,8 +624,7 @@ sockunion_getpeername (int fd)
ret = getpeername (fd, (struct sockaddr *)&name, &len);
if (ret < 0)
{
- zlog (NULL, LOG_WARNING, "Can't get remote address and port: %s",
- safe_strerror (errno));
+ zlog_warn("Can't get remote address and port: %s", safe_strerror(errno));
return NULL;
}
diff --git a/lib/thread.c b/lib/thread.c
index c1558a83e1..6138e79718 100644
--- a/lib/thread.c
+++ b/lib/thread.c
@@ -719,7 +719,8 @@ funcname_thread_add_read_write (int dir, struct thread_master *m,
#else
if (FD_ISSET (fd, fdset))
{
- zlog (NULL, LOG_WARNING, "There is already %s fd [%d]", (dir == THREAD_READ) ? "read" : "write", fd);
+ zlog_warn ("There is already %s fd [%d]",
+ (dir == THREAD_READ) ? "read" : "write", fd);
return NULL;
}
diff --git a/lib/thread.h b/lib/thread.h
index 0489246ea6..34adcc4d09 100644
--- a/lib/thread.h
+++ b/lib/thread.h
@@ -246,11 +246,6 @@ extern void thread_cmd_init (void);
extern unsigned long thread_consumed_time(RUSAGE_T *after, RUSAGE_T *before,
unsigned long *cpu_time_elapsed);
-/* Global variable containing a recent result from gettimeofday. This can
- be used instead of calling gettimeofday if a recent value is sufficient.
- This is guaranteed to be refreshed before a thread is called. */
-extern struct timeval recent_time;
-
/* only for use in logging functions! */
extern struct thread *thread_current;
diff --git a/lib/vty.c b/lib/vty.c
index 3a3265f54d..4d34fead8b 100644
--- a/lib/vty.c
+++ b/lib/vty.c
@@ -214,7 +214,7 @@ vty_time_print (struct vty *vty, int cr)
if (quagga_timestamp(0, buf, sizeof(buf)) == 0)
{
- zlog (NULL, LOG_INFO, "quagga_timestamp error");
+ zlog_info("quagga_timestamp error");
return;
}
if (cr)
@@ -437,7 +437,7 @@ vty_command (struct vty *vty, char *buf)
snprintf(prompt_str, sizeof(prompt_str), cmd_prompt (vty->node), vty_str);
/* now log the command */
- zlog(NULL, LOG_ERR, "%s%s", prompt_str, buf);
+ zlog_err("%s%s", prompt_str, buf);
}
/* Split readline string up into the vector */
vline = cmd_make_strvec (buf);
@@ -457,10 +457,7 @@ vty_command (struct vty *vty, char *buf)
ret = cmd_execute_command (vline, vty, NULL, 0);
/* Get the name of the protocol if any */
- if (zlog_default)
- protocolname = zlog_proto_names[zlog_default->protocol];
- else
- protocolname = zlog_proto_names[ZLOG_NONE];
+ protocolname = zlog_protoname();
#ifdef CONSUMED_TIME_CHECK
GETRUSAGE(&after);
@@ -1856,8 +1853,8 @@ vty_accept (struct thread *thread)
if ((acl = access_list_lookup (AFI_IP, vty_accesslist_name)) &&
(access_list_apply (acl, &p) == FILTER_DENY))
{
- zlog (NULL, LOG_INFO, "Vty connection refused from %s",
- sockunion2str (&su, buf, SU_ADDRSTRLEN));
+ zlog_info ("Vty connection refused from %s",
+ sockunion2str (&su, buf, SU_ADDRSTRLEN));
close (vty_sock);
/* continue accepting connections */
@@ -1873,8 +1870,8 @@ vty_accept (struct thread *thread)
if ((acl = access_list_lookup (AFI_IP6, vty_ipv6_accesslist_name)) &&
(access_list_apply (acl, &p) == FILTER_DENY))
{
- zlog (NULL, LOG_INFO, "Vty connection refused from %s",
- sockunion2str (&su, buf, SU_ADDRSTRLEN));
+ zlog_info ("Vty connection refused from %s",
+ sockunion2str (&su, buf, SU_ADDRSTRLEN));
close (vty_sock);
/* continue accepting connections */
@@ -1888,11 +1885,11 @@ vty_accept (struct thread *thread)
ret = setsockopt (vty_sock, IPPROTO_TCP, TCP_NODELAY,
(char *) &on, sizeof (on));
if (ret < 0)
- zlog (NULL, LOG_INFO, "can't set sockopt to vty_sock : %s",
- safe_strerror (errno));
+ zlog_info ("can't set sockopt to vty_sock : %s",
+ safe_strerror (errno));
- zlog (NULL, LOG_INFO, "Vty connection from %s",
- sockunion2str (&su, buf, SU_ADDRSTRLEN));
+ zlog_info ("Vty connection from %s",
+ sockunion2str (&su, buf, SU_ADDRSTRLEN));
vty_create (vty_sock, &su);
@@ -2354,7 +2351,7 @@ vty_read_file (FILE *confp)
}
static FILE *
-vty_use_backup_config (char *fullpath)
+vty_use_backup_config (const char *fullpath)
{
char *fullpath_sav, *fullpath_tmp;
FILE *ret = NULL;
@@ -2413,12 +2410,12 @@ out_close_sav:
/* Read up configuration file from file_name. */
void
-vty_read_config (char *config_file,
+vty_read_config (const char *config_file,
char *config_default_dir)
{
char cwd[MAXPATHLEN];
FILE *confp = NULL;
- char *fullpath;
+ const char *fullpath;
char *tmp = NULL;
/* If -f flag specified. */
@@ -2518,7 +2515,7 @@ vty_read_config (char *config_file,
tmp_free_and_out:
if (tmp)
- XFREE (MTYPE_TMP, fullpath);
+ XFREE (MTYPE_TMP, tmp);
}
/* Small utility function which output log to the VTY. */
diff --git a/lib/vty.h b/lib/vty.h
index 72a4f6394d..0ac73d95be 100644
--- a/lib/vty.h
+++ b/lib/vty.h
@@ -324,7 +324,7 @@ extern void vty_reset (void);
extern struct vty *vty_new (void);
extern struct vty *vty_stdio (void (*atclose)(void));
extern int vty_out (struct vty *, const char *, ...) PRINTF_ATTRIBUTE(2, 3);
-extern void vty_read_config (char *, char *);
+extern void vty_read_config (const char *, char *);
extern void vty_time_print (struct vty *, int);
extern void vty_serv_sock (const char *, unsigned short, const char *);
extern void vty_close (struct vty *);
diff --git a/nhrpd/Makefile.am b/nhrpd/Makefile.am
new file mode 100644
index 0000000000..ae0dd6f5f8
--- /dev/null
+++ b/nhrpd/Makefile.am
@@ -0,0 +1,47 @@
+## Process this file with automake to produce Makefile.in.
+
+AM_CPPFLAGS = -I.. -I$(top_srcdir) -I$(top_srcdir)/lib -I$(top_builddir)/lib -DQUAGGA_NO_DEPRECATED_INTERFACES
+DEFS = @DEFS@ @CARES_CFLAGS@ -DSYSCONFDIR=\"$(sysconfdir)/\"
+INSTALL_SDATA=@INSTALL@ -m 600
+
+AM_CFLAGS = $(PICFLAGS) #$(WERROR)
+AM_LDFLAGS = $(PICLDFLAGS)
+
+sbin_PROGRAMS = nhrpd
+
+nhrpd_SOURCES = \
+ zbuf.c \
+ znl.c \
+ resolver.c \
+ linux.c \
+ netlink_arp.c \
+ netlink_gre.c \
+ vici.c \
+ reqid.c \
+ nhrp_event.c \
+ nhrp_packet.c \
+ nhrp_interface.c \
+ nhrp_vc.c \
+ nhrp_peer.c \
+ nhrp_cache.c \
+ nhrp_nhs.c \
+ nhrp_route.c \
+ nhrp_shortcut.c \
+ nhrp_vty.c \
+ nhrp_main.c
+
+nhrpd_LDADD = ../lib/libfrr.la @LIBCAP@ @CARES_LIBS@
+
+noinst_HEADERS = \
+ debug.h \
+ list.h \
+ netlink.h \
+ nhrp_protocol.h \
+ nhrpd.h \
+ os.h \
+ vici.h \
+ zbuf.h \
+ znl.h \
+ # end
+
+#dist_examples_DATA = nhrpd.conf.sample
diff --git a/nhrpd/README.kernel b/nhrpd/README.kernel
new file mode 100644
index 0000000000..5831316f1f
--- /dev/null
+++ b/nhrpd/README.kernel
@@ -0,0 +1,145 @@
+KERNEL REQUIREMENTS
+===================
+
+The linux kernel has had various major regressions, performance
+issues and subtle bugs (especially in pmtu). Here is a short list
+of some -stable kernels and the first point release that is supposedly
+working well with opennhrp/dmvpn:
+ 3.12.8 or later
+ 3.14.54 or later
+ 3.18.22 or later[1]
+
+[1] But you need to apply the following two backported commits:
+ 3cdaa5be9e ipv4: Don't increase PMTU with Datagram Too Big message
+ cb6ccf09d6 route: Use ipv4_mtu instead of raw rt_pmtu
+
+See below for list of known issues in various kernel versions.
+
+Kernels earlier than 3.12 need CONFIG_ARPD enabled in the configuration.
+Many distributions do not enable it by default, and you may need to
+compile your own kernel.
+
+KERNEL BUGS
+===========
+
+DMVPN and mGRE support in the kernel has been brittle. There are various
+regressions in multiple kernel versions.
+
+This list tries to collect them to one source of information:
+
+- forward pmtu is disabled intentionally (but tunnel devices rely on it)
+ Broken since 3.14-rc1:
+ commit "ipv4: introduce ip_dst_mtu_maybe_forward and protect forwarding path against pmtu spoofing"
+ Workaround:
+ Set sysctl net.ipv4.ip_forward_use_pmtu=1
+ (Should fix kernel to have this by default on for tunnel devices)
+
+- subtle path mtu mishandling issues
+ Broken since (uncertain)
+ Fixed in 4.1-rc2:
+ commit "ipv4: Don't increase PMTU with Datagram Too Big message."
+ commit "route: Use ipv4_mtu instead of raw rt_pmtu"
+
+- fragmentation of large packets inside tunnel not working
+ Broken since 3.11-rc1
+ commit "ip_tunnels: Use skb-len to PMTU check."
+ Fixed in 3.14.54, 3.18.22, 4.1.9, 4.2-rc3
+ commit "ip_tunnel: fix ipv4 pmtu check to honor inner ip header df"
+
+- ipsec will crash during xfrm gc
+ Broke since 3.15-rc1
+ commit "flowcache: Make flow cache name space aware"
+ Fixed in 3.18.10, 4.0
+ commit "flowcache: Fix kernel panic in flow_cache_flush_task"
+
+- TSO on GRE tunnels failed, and resulted in very slow performance
+ Broke since 3.14.24, 3.18-rc3
+ commit "gre: Use inner mac length when computing tunnel length"
+ Fixed in 3.14.30, 3.18.4
+ commit "gre: fix the inner mac header in nbma tunnel xmit path"
+ commit "gre: Set inner mac header in gro complete"
+
+- NAPI GRO handling was broken; causing immediate crash (32-bit only?)
+ Broken since 3.13-rc1
+ commit "net: gro: allow to build full sized skb"
+ Fixed 3.14.5, 3.15-rc7
+ commit "net: gro: make sure skb->cb[] initial content has not to be zero"
+
+- ip_gre dst caching broke NBMA GRE tunnels
+ Broken since 3.14-rc1
+ Fixed in 3.14.5, 3.15-rc6
+ commit "ipv4: ip_tunnels: disable cache for nbma gre tunnels"
+
+- Few packets can be lost when neighbor entry is in NUD_PROBE state,
+ and there is continuous traffic to it.
+ Broken since dawn of time
+ Fixed in 3.15-rc1
+ commit "neigh: probe application via netlink in NUD_PROBE"
+
+- GRO was implemented for GRE, but the hw capabilities were not updated
+ correctly. In practice forwarding from non-GRE (physical) interface
+ to GRE interface with gro/gso/tx offloads enabled (also on the target
+ interface) does not work properly.
+ Broken around 3.9 to 3.11, need to check details.
+
+- recvfrom() returned incorrect NBMA address, breaking NAT detection
+ Broken since 3.10-rc1
+ commit "GRE: Refactor GRE tunneling code."
+ Fixed in 3.10.27, 3.12.8, 3.13-rc7
+ commit "ip_gre: fix msg_name parsing for recvfrom/recvmsg"
+
+- sendto() was broken causing opennhrp not work at all
+ Broken since 3.10-rc1
+ commit "GRE: Refactor GRE tunneling code."
+ Fixed in 3.10.12, 3.11-rc6
+ commit "ip_gre: fix ipgre_header to return correct offset"
+
+- PMTU was broken due to GRE driver rewrite
+ Broken since 3.10-rc1
+ commit "GRE: Refactor GRE tunneling code."
+ Fixed in 3.11-rc1
+ commit "ip_tunnels: Use skb-len to PMTU check."
+
+- PMTU was broken due to routing cache removal
+ Broken since 3.6-rc1
+ commit "ipv4: Cache input routes in fib_info nexthops"
+ Fixed in 3.11-rc1
+ commit "ipv4: use next hop exceptions also for input routes"
+ + 3 other commits
+ Patches exist for 3.10, but they were not approved to 3.10-stable.
+
+- Race condition during bootup: changing ARP flag did not flush
+ existing neighbor entries, causing problems if traffic was routed
+ to gre interface before opennhrp was running.
+ Broken since dawn of time
+ Fixed in 3.11-rc1
+ commit "arp: flush arp cache on IFF_NOARP change"
+
+- Crash in IPsec
+ Broken since 3.9-rc1
+ commit "xfrm: removes a superfluous check and add a statistic"
+ Fixed in 3.10-rc3
+ commit "xfrm: properly handle invalid states as an error"
+
+- An incorrect ip_gre change broke NHRP traffic over GRE
+ Broken since 3.8-rc2
+ commit "ip_gre: make ipgre_tunnel_xmit() not parse network header as IP unconditionally"
+ Fixed in 3.8.5, 3.9-rc4
+ commit "Revert "ip_gre: make ipgre_tunnel_xmit() not parse network header as IP unconditionally""
+
+- Multicast traffic over mGRE was broken.
+ Broken since 2.6.34-rc2
+ commit "gre: fix hard header destination address checking"
+ Fixed in 2.6.39-rc2
+ commit "net: gre: provide multicast mappings for ipv4 and ipv6"
+
+- Serious performance issues causing small throughput on medium to large DMVPN networks
+ Broken since dawn of time
+ Fixed in 2.6.35
+ multiple commits rewriting ipsec caching
+
+- Even though around 2.6.24 is the first version where opennhrp started
+ to work, there has been various PMTU, performance, and functionality
+ bugs before 2.6.34. That's one of the first version I consider stable
+ wrt. to opennhrp functionality.
+
diff --git a/nhrpd/README.nhrpd b/nhrpd/README.nhrpd
new file mode 100644
index 0000000000..569b3f4463
--- /dev/null
+++ b/nhrpd/README.nhrpd
@@ -0,0 +1,137 @@
+Quagga / NHRP Design and Configuration Notes
+============================================
+
+Quagga/NHRP is an NHRP (RFC2332) implementation for Linux. The primary
+use case is to implement DMVPN. The aim is thus to be compatible with
+Cisco DMVPN (and potentially with FlexVPN in the future).
+
+
+Current Status
+--------------
+
+- IPsec integration with strongSwan (requires patched strongSwan)
+- IPv4 over IPv4 NBMA GRE
+- IPv6 over IPv4 NBMA GRE -- majority of code exist; but is not tested
+- Spoke (NHC) functionality complete
+- Hub (NHS) functionality complete
+- Multicast support is not done yet
+ (so OSPF will not work, use BGP for now)
+
+The code is not (yet) compatible with Cisco FlexVPN style DMVPN. It
+would require relaying IKEv2 routing messages from strongSwan to nhrpd
+and parsing that. It is doable, but not implemented for the time being.
+
+
+Routing Design
+--------------
+
+In contrast to opennhrp routing design, Quagga/NHRP routes each NHRP
+domain address individually (similar to Cisco FlexVPN).
+
+To create NBMA GRE tunnel you might use following:
+ ip tunnel add gre1 mode gre key 42 ttl 64 dev eth0
+ ip addr add 10.255.255.2/32 dev gre1
+ ip link set gre1 up
+
+This has two important differences compared to opennhrp setup:
+ 1. The 'tunnel add' now specifies physical device binding. Quagga/NHRP
+ wants to know stable protocol address to NBMA address mapping. Thus,
+ add 'dev <physdev>' binding, or specify 'local <nbma-address>'. If
+ neither of this is specified, NHRP will not be enabled on the interface.
+ Alternatively you can skip 'dev' binding on tunnel if you allow
+ nhrpd to manage it using 'tunnel source' command (see below).
+
+ 2. The 'addr add' now has host prefix. In opennhrp you would have used
+ the GRE subnet prefix length here instead, e.g. /24.
+
+Quagga/NHRP will automatically create additional host routes pointing to
+gre1 when a connection with these hosts is established. The gre1 subnet
+should be announced by routing protocol. This allows routing protocol
+to decide which is the closest hub and get the gre addresses' traffic.
+
+The second benefit is that hubs can then easily exchange host prefixes
+of directly connected gre addresses. And thus routing of gre addresses
+inside hubs is based on routing protocol's shortest path choice -- not
+on random choice from next hop server list.
+
+
+Configuring nhrpd
+-----------------
+
+The configuration is done using vtysh, and most commands do what they
+do in Cisco. As minimal configuration example one can do:
+ configure terminal
+ interface gre1
+ tunnel protection vici profile dmvpn
+ tunnel source eth0
+ ip nhrp network-id 1
+ ip nhrp shortcut
+ ip nhrp registration no-unique
+ ip nhrp nhs dynamic nbma hubs.example.com
+
+There's important notes about the "ip nhrp nhs" command:
+
+ 1. The 'dynamic' works only against Cisco (or nhrpd), but is not
+ compatible with opennhrp. To use dynamic detection of opennhrp hub's
+ protocol address use the GRE broadcast address there. For the above
+ example of 10.255.255.0/24 the configuration should read instead:
+ ip nhrp nhs 10.255.255.255 nbma hubs.example.com
+
+ 2. nbma <FQDN> works like opennhrp dynamic-map. That is, all of the
+ A-records are configured as NBMA addresses of different hubs, and
+ each hub protocol address will be dynamically detected.
+
+
+Hub functionality
+-----------------
+
+Sending Traffic Indication (redirect) notifications is now accomplished
+using NFLOG.
+
+Use:
+iptables -A FORWARD -i gre1 -o gre1 \
+ -m hashlimit --hashlimit-upto 4/minute --hashlimit-burst 1 \
+ --hashlimit-mode srcip,dstip --hashlimit-srcmask 16 --hashlimit-dstmask 16 \
+ --hashlimit-name loglimit-0 -j NFLOG --nflog-group 1 --nflog-range 128
+
+or similar to get rate-limited samples of the packets that match traffic
+flow needing redirection. This kernel NFLOG target's nflog-group is configured
+in global nhrp config with:
+ nhrp nflog-group 1
+
+To start sending these traffic notices out from hubs, use the nhrp per-interface
+directive:
+ ip nhrp redirect
+
+opennhrp used PF_PACKET and tried to create packet filter to get only
+the packets of interest. Though, this was bad if shortcut fails to
+establish (remote policy, or both are behind NAT or restrictive
+firewalls), all of the relayaed traffic would match always.
+
+
+Getting information via vtysh
+-----------------------------
+
+Some commands of interest:
+ - show dmvpn
+ - show ip nhrp cache
+ - show ip nhrp shortcut
+ - show ip route nhrp
+ - clear ip nhrp cache
+ - clear ip nhrp shortcut
+
+
+Integration with strongSwan
+---------------------------
+
+Contrary to opennhrp, Quagga/NHRP has tight integration with IKE daemon.
+Currently strongSwan is supported using the VICI protocol. strongSwan
+is connected using UNIX socket (hardcoded now as /var/run/charon.vici).
+Thus nhrpd needs to be run as user that can open that file.
+
+Currently, you will need patched strongSwan. The working tree is at:
+ http://git.alpinelinux.org/cgit/user/tteras/strongswan/log/?h=tteras
+
+And the branch with patches against latest release are:
+ http://git.alpinelinux.org/cgit/user/tteras/strongswan/log/?h=tteras-release
+
diff --git a/nhrpd/debug.h b/nhrpd/debug.h
new file mode 100644
index 0000000000..b1f49aa8b7
--- /dev/null
+++ b/nhrpd/debug.h
@@ -0,0 +1,42 @@
+#include "log.h"
+
+#if defined(__GNUC__) && (__GNUC__ >= 3)
+#define likely(_x) __builtin_expect(!!(_x), 1)
+#define unlikely(_x) __builtin_expect(!!(_x), 0)
+#else
+#define likely(_x) !!(_x)
+#define unlikely(_x) !!(_x)
+#endif
+
+#define NHRP_DEBUG_COMMON (1 << 0)
+#define NHRP_DEBUG_KERNEL (1 << 1)
+#define NHRP_DEBUG_IF (1 << 2)
+#define NHRP_DEBUG_ROUTE (1 << 3)
+#define NHRP_DEBUG_VICI (1 << 4)
+#define NHRP_DEBUG_EVENT (1 << 5)
+#define NHRP_DEBUG_ALL (0xFFFF)
+
+extern unsigned int debug_flags;
+
+#if defined __STDC_VERSION__ && __STDC_VERSION__ >= 199901L
+
+#define debugf(level, ...) \
+ do { \
+ if (unlikely(debug_flags & level)) \
+ zlog_debug(__VA_ARGS__); \
+ } while(0)
+
+#elif defined __GNUC__
+
+#define debugf(level, _args...) \
+ do { \
+ if (unlikely(debug_flags & level)) \
+ zlog_debug(_args); \
+ } while(0)
+
+#else
+
+static inline void debugf(int level, const char *format, ...) { }
+
+#endif
+
diff --git a/nhrpd/linux.c b/nhrpd/linux.c
new file mode 100644
index 0000000000..1e9c69eb86
--- /dev/null
+++ b/nhrpd/linux.c
@@ -0,0 +1,153 @@
+/* NHRP daemon Linux specific glue
+ * Copyright (c) 2014-2015 Timo Teräs
+ *
+ * This file is free software: you may copy, redistribute 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.
+ */
+
+#include <fcntl.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <asm/types.h>
+#include <arpa/inet.h>
+#include <linux/netlink.h>
+#include <linux/rtnetlink.h>
+#include <linux/ip.h>
+#include <linux/if_arp.h>
+#include <linux/if_tunnel.h>
+
+#include "nhrp_protocol.h"
+#include "os.h"
+#include "netlink.h"
+
+static int nhrp_socket_fd = -1;
+
+int os_socket(void)
+{
+ if (nhrp_socket_fd < 0)
+ nhrp_socket_fd = socket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_NHRP));
+ return nhrp_socket_fd;
+}
+
+int os_sendmsg(const uint8_t *buf, size_t len, int ifindex, const uint8_t *addr, size_t addrlen)
+{
+ struct sockaddr_ll lladdr;
+ struct iovec iov = {
+ .iov_base = (void*) buf,
+ .iov_len = len,
+ };
+ struct msghdr msg = {
+ .msg_name = &lladdr,
+ .msg_namelen = sizeof(lladdr),
+ .msg_iov = &iov,
+ .msg_iovlen = 1,
+ };
+ int status;
+
+ if (addrlen > sizeof(lladdr.sll_addr))
+ return -1;
+
+ memset(&lladdr, 0, sizeof(lladdr));
+ lladdr.sll_family = AF_PACKET;
+ lladdr.sll_protocol = htons(ETH_P_NHRP);
+ lladdr.sll_ifindex = ifindex;
+ lladdr.sll_halen = addrlen;
+ memcpy(lladdr.sll_addr, addr, addrlen);
+
+ status = sendmsg(nhrp_socket_fd, &msg, 0);
+ if (status < 0)
+ return -1;
+
+ return 0;
+}
+
+int os_recvmsg(uint8_t *buf, size_t *len, int *ifindex, uint8_t *addr, size_t *addrlen)
+{
+ struct sockaddr_ll lladdr;
+ struct iovec iov = {
+ .iov_base = buf,
+ .iov_len = *len,
+ };
+ struct msghdr msg = {
+ .msg_name = &lladdr,
+ .msg_namelen = sizeof(lladdr),
+ .msg_iov = &iov,
+ .msg_iovlen = 1,
+ };
+ int r;
+
+ r = recvmsg(nhrp_socket_fd, &msg, MSG_DONTWAIT);
+ if (r < 0)
+ return r;
+
+ *len = r;
+ *ifindex = lladdr.sll_ifindex;
+
+ if (*addrlen <= (size_t) lladdr.sll_addr) {
+ if (memcmp(lladdr.sll_addr, "\x00\x00\x00\x00", 4) != 0) {
+ memcpy(addr, lladdr.sll_addr, lladdr.sll_halen);
+ *addrlen = lladdr.sll_halen;
+ } else {
+ *addrlen = 0;
+ }
+ }
+
+ return 0;
+}
+
+static int linux_configure_arp(const char *iface, int on)
+{
+ struct ifreq ifr;
+
+ strncpy(ifr.ifr_name, iface, IFNAMSIZ);
+ if (ioctl(nhrp_socket_fd, SIOCGIFFLAGS, &ifr))
+ return -1;
+
+ if (on)
+ ifr.ifr_flags &= ~IFF_NOARP;
+ else
+ ifr.ifr_flags |= IFF_NOARP;
+
+ if (ioctl(nhrp_socket_fd, SIOCSIFFLAGS, &ifr))
+ return -1;
+
+ return 0;
+}
+
+static int linux_icmp_redirect_off(const char *iface)
+{
+ char fname[256];
+ int fd, ret = -1;
+
+ sprintf(fname, "/proc/sys/net/ipv4/conf/%s/send_redirects", iface);
+ fd = open(fname, O_WRONLY);
+ if (fd < 0)
+ return -1;
+ if (write(fd, "0\n", 2) == 2)
+ ret = 0;
+ close(fd);
+
+ return ret;
+}
+
+int os_configure_dmvpn(unsigned int ifindex, const char *ifname, int af)
+{
+ int ret = -1;
+
+ switch (af) {
+ case AF_INET:
+ ret = linux_icmp_redirect_off("all");
+ ret |= linux_icmp_redirect_off(ifname);
+ ret |= netlink_configure_arp(ifindex, AF_INET);
+ ret |= linux_configure_arp(ifname, 1);
+ break;
+ }
+
+ return ret;
+}
diff --git a/nhrpd/list.h b/nhrpd/list.h
new file mode 100644
index 0000000000..32f21ed5e5
--- /dev/null
+++ b/nhrpd/list.h
@@ -0,0 +1,191 @@
+/* Linux kernel style list handling function
+ *
+ * Written from scratch by Timo Teräs <timo.teras@iki.fi>, but modeled
+ * after the linux kernel code.
+ *
+ * This file is free software: you may copy, redistribute 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.
+ */
+
+#ifndef LIST_H
+#define LIST_H
+
+#ifndef NULL
+#define NULL 0L
+#endif
+
+#ifndef offsetof
+#ifdef __compiler_offsetof
+#define offsetof(TYPE,MEMBER) __compiler_offsetof(TYPE,MEMBER)
+#else
+#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
+#endif
+#endif
+
+#ifndef container_of
+#define container_of(ptr, type, member) ({ \
+ const typeof( ((type *)0)->member ) *__mptr = (ptr); \
+ (type *)( (char *)__mptr - offsetof(type,member) );})
+#endif
+
+struct hlist_head {
+ struct hlist_node *first;
+};
+
+struct hlist_node {
+ struct hlist_node *next;
+ struct hlist_node **pprev;
+};
+
+static inline int hlist_empty(const struct hlist_head *h)
+{
+ return !h->first;
+}
+
+static inline int hlist_hashed(const struct hlist_node *n)
+{
+ return n->pprev != NULL;
+}
+
+static inline void hlist_del(struct hlist_node *n)
+{
+ struct hlist_node *next = n->next;
+ struct hlist_node **pprev = n->pprev;
+
+ *pprev = next;
+ if (next)
+ next->pprev = pprev;
+
+ n->next = NULL;
+ n->pprev = NULL;
+}
+
+static inline void hlist_add_head(struct hlist_node *n, struct hlist_head *h)
+{
+ struct hlist_node *first = h->first;
+
+ n->next = first;
+ if (first)
+ first->pprev = &n->next;
+ n->pprev = &h->first;
+ h->first = n;
+}
+
+static inline void hlist_add_after(struct hlist_node *n, struct hlist_node *prev)
+{
+ n->next = prev->next;
+ n->pprev = &prev->next;
+ prev->next = n;
+}
+
+static inline struct hlist_node **hlist_tail_ptr(struct hlist_head *h)
+{
+ struct hlist_node *n = h->first;
+ if (n == NULL)
+ return &h->first;
+ while (n->next != NULL)
+ n = n->next;
+ return &n->next;
+}
+
+#define hlist_entry(ptr, type, member) container_of(ptr,type,member)
+
+#define hlist_for_each(pos, head) \
+ for (pos = (head)->first; pos; pos = pos->next)
+
+#define hlist_for_each_safe(pos, n, head) \
+ for (pos = (head)->first; pos && ({ n = pos->next; 1; }); pos = n)
+
+#define hlist_for_each_entry(tpos, pos, head, member) \
+ for (pos = (head)->first; pos && \
+ ({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \
+ pos = pos->next)
+
+#define hlist_for_each_entry_safe(tpos, pos, n, head, member) \
+ for (pos = (head)->first; \
+ pos && ({ n = pos->next; 1; }) && \
+ ({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \
+ pos = n)
+
+
+struct list_head {
+ struct list_head *next, *prev;
+};
+
+#define LIST_INITIALIZER(l) { .next = &l, .prev = &l }
+
+static inline void list_init(struct list_head *list)
+{
+ list->next = list;
+ list->prev = list;
+}
+
+static inline void __list_add(struct list_head *new,
+ struct list_head *prev,
+ struct list_head *next)
+{
+ next->prev = new;
+ new->next = next;
+ new->prev = prev;
+ prev->next = new;
+}
+
+static inline void list_add(struct list_head *new, struct list_head *head)
+{
+ __list_add(new, head, head->next);
+}
+
+static inline void list_add_tail(struct list_head *new, struct list_head *head)
+{
+ __list_add(new, head->prev, head);
+}
+
+static inline void __list_del(struct list_head * prev, struct list_head * next)
+{
+ next->prev = prev;
+ prev->next = next;
+}
+
+static inline void list_del(struct list_head *entry)
+{
+ __list_del(entry->prev, entry->next);
+ entry->next = NULL;
+ entry->prev = NULL;
+}
+
+static inline int list_hashed(const struct list_head *n)
+{
+ return n->next != n && n->next != NULL;
+}
+
+static inline int list_empty(const struct list_head *n)
+{
+ return !list_hashed(n);
+}
+
+#define list_next(ptr, type, member) \
+ (list_hashed(ptr) ? container_of((ptr)->next,type,member) : NULL)
+
+#define list_entry(ptr, type, member) container_of(ptr,type,member)
+
+#define list_for_each(pos, head) \
+ for (pos = (head)->next; pos != (head); pos = pos->next)
+
+#define list_for_each_safe(pos, n, head) \
+ for (pos = (head)->next, n = pos->next; pos != (head); \
+ pos = n, n = pos->next)
+
+#define list_for_each_entry(pos, head, member) \
+ for (pos = list_entry((head)->next, typeof(*pos), member); \
+ &pos->member != (head); \
+ pos = list_entry(pos->member.next, typeof(*pos), member))
+
+#define list_for_each_entry_safe(pos, n, head, member) \
+ for (pos = list_entry((head)->next, typeof(*pos), member), \
+ n = list_entry(pos->member.next, typeof(*pos), member); \
+ &pos->member != (head); \
+ pos = n, n = list_entry(n->member.next, typeof(*n), member))
+
+#endif
diff --git a/nhrpd/netlink.h b/nhrpd/netlink.h
new file mode 100644
index 0000000000..f05596ba1b
--- /dev/null
+++ b/nhrpd/netlink.h
@@ -0,0 +1,24 @@
+/* NHRP netlink/neighbor table API
+ * Copyright (c) 2014-2015 Timo Teräs
+ *
+ * This file is free software: you may copy, redistribute 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.
+ */
+
+#include <stdint.h>
+
+union sockunion;
+struct interface;
+
+extern int netlink_nflog_group;
+extern int netlink_req_fd;
+
+int netlink_init(void);
+int netlink_configure_arp(unsigned int ifindex, int pf);
+void netlink_update_binding(struct interface *ifp, union sockunion *proto, union sockunion *nbma);
+void netlink_set_nflog_group(int nlgroup);
+
+void netlink_gre_get_info(unsigned int ifindex, uint32_t *gre_key, unsigned int *link_index, struct in_addr *saddr);
+void netlink_gre_set_link(unsigned int ifindex, unsigned int link_index);
diff --git a/nhrpd/netlink_arp.c b/nhrpd/netlink_arp.c
new file mode 100644
index 0000000000..a418ecabd3
--- /dev/null
+++ b/nhrpd/netlink_arp.c
@@ -0,0 +1,275 @@
+/* NHRP netlink/neighbor table arpd code
+ * Copyright (c) 2014-2016 Timo Teräs
+ *
+ * This file is free software: you may copy, redistribute 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.
+ */
+
+#include <fcntl.h>
+#include <net/if.h>
+#include <netinet/if_ether.h>
+#include <linux/netlink.h>
+#include <linux/neighbour.h>
+#include <linux/netfilter/nfnetlink_log.h>
+
+#include "thread.h"
+#include "nhrpd.h"
+#include "netlink.h"
+#include "znl.h"
+
+int netlink_req_fd = -1;
+int netlink_nflog_group;
+static int netlink_log_fd = -1;
+static struct thread *netlink_log_thread;
+static int netlink_listen_fd = -1;
+
+typedef void (*netlink_dispatch_f)(struct nlmsghdr *msg, struct zbuf *zb);
+
+void netlink_update_binding(struct interface *ifp, union sockunion *proto, union sockunion *nbma)
+{
+ struct nlmsghdr *n;
+ struct ndmsg *ndm;
+ struct zbuf *zb = zbuf_alloc(512);
+
+ n = znl_nlmsg_push(zb, nbma ? RTM_NEWNEIGH : RTM_DELNEIGH, NLM_F_REQUEST | NLM_F_REPLACE | NLM_F_CREATE);
+ ndm = znl_push(zb, sizeof(*ndm));
+ *ndm = (struct ndmsg) {
+ .ndm_family = sockunion_family(proto),
+ .ndm_ifindex = ifp->ifindex,
+ .ndm_type = RTN_UNICAST,
+ .ndm_state = nbma ? NUD_REACHABLE : NUD_FAILED,
+ };
+ znl_rta_push(zb, NDA_DST, sockunion_get_addr(proto), family2addrsize(sockunion_family(proto)));
+ if (nbma)
+ znl_rta_push(zb, NDA_LLADDR, sockunion_get_addr(nbma), family2addrsize(sockunion_family(nbma)));
+ znl_nlmsg_complete(zb, n);
+ zbuf_send(zb, netlink_req_fd);
+ zbuf_recv(zb, netlink_req_fd);
+ zbuf_free(zb);
+}
+
+static void netlink_neigh_msg(struct nlmsghdr *msg, struct zbuf *zb)
+{
+ struct ndmsg *ndm;
+ struct rtattr *rta;
+ struct nhrp_cache *c;
+ struct interface *ifp;
+ struct zbuf payload;
+ union sockunion addr;
+ size_t len;
+ char buf[SU_ADDRSTRLEN];
+ int state;
+
+ ndm = znl_pull(zb, sizeof(*ndm));
+ if (!ndm) return;
+
+ sockunion_family(&addr) = AF_UNSPEC;
+ while ((rta = znl_rta_pull(zb, &payload)) != NULL) {
+ len = zbuf_used(&payload);
+ switch (rta->rta_type) {
+ case NDA_DST:
+ sockunion_set(&addr, ndm->ndm_family, zbuf_pulln(&payload, len), len);
+ break;
+ }
+ }
+
+ ifp = if_lookup_by_index(ndm->ndm_ifindex);
+ if (!ifp || sockunion_family(&addr) == AF_UNSPEC)
+ return;
+
+ c = nhrp_cache_get(ifp, &addr, 0);
+ if (!c)
+ return;
+
+ if (msg->nlmsg_type == RTM_GETNEIGH) {
+ debugf(NHRP_DEBUG_KERNEL, "Netlink: who-has %s dev %s",
+ sockunion2str(&addr, buf, sizeof buf),
+ ifp->name);
+
+ if (c->cur.type >= NHRP_CACHE_CACHED) {
+ nhrp_cache_set_used(c, 1);
+ netlink_update_binding(ifp, &addr, &c->cur.peer->vc->remote.nbma);
+ }
+ } else {
+ debugf(NHRP_DEBUG_KERNEL, "Netlink: update %s dev %s nud %x",
+ sockunion2str(&addr, buf, sizeof buf),
+ ifp->name, ndm->ndm_state);
+
+ state = (msg->nlmsg_type == RTM_NEWNEIGH) ? ndm->ndm_state : NUD_FAILED;
+ nhrp_cache_set_used(c, state == NUD_REACHABLE);
+ }
+}
+
+static int netlink_route_recv(struct thread *t)
+{
+ uint8_t buf[ZNL_BUFFER_SIZE];
+ int fd = THREAD_FD(t);
+ struct zbuf payload, zb;
+ struct nlmsghdr *n;
+
+ zbuf_init(&zb, buf, sizeof(buf), 0);
+ while (zbuf_recv(&zb, fd) > 0) {
+ while ((n = znl_nlmsg_pull(&zb, &payload)) != 0) {
+ debugf(NHRP_DEBUG_KERNEL, "Netlink: Received msg_type %u, msg_flags %u",
+ n->nlmsg_type, n->nlmsg_flags);
+ switch (n->nlmsg_type) {
+ case RTM_GETNEIGH:
+ case RTM_NEWNEIGH:
+ case RTM_DELNEIGH:
+ netlink_neigh_msg(n, &payload);
+ break;
+ }
+ }
+ }
+
+ thread_add_read(master, netlink_route_recv, 0, fd);
+
+ return 0;
+}
+
+static void netlink_log_register(int fd, int group)
+{
+ struct nlmsghdr *n;
+ struct nfgenmsg *nf;
+ struct nfulnl_msg_config_cmd cmd;
+ struct zbuf *zb = zbuf_alloc(512);
+
+ n = znl_nlmsg_push(zb, (NFNL_SUBSYS_ULOG<<8) | NFULNL_MSG_CONFIG, NLM_F_REQUEST | NLM_F_ACK);
+ nf = znl_push(zb, sizeof(*nf));
+ *nf = (struct nfgenmsg) {
+ .nfgen_family = AF_UNSPEC,
+ .version = NFNETLINK_V0,
+ .res_id = htons(group),
+ };
+ cmd.command = NFULNL_CFG_CMD_BIND;
+ znl_rta_push(zb, NFULA_CFG_CMD, &cmd, sizeof(cmd));
+ znl_nlmsg_complete(zb, n);
+
+ zbuf_send(zb, fd);
+ zbuf_free(zb);
+}
+
+static void netlink_log_indication(struct nlmsghdr *msg, struct zbuf *zb)
+{
+ struct nfgenmsg *nf;
+ struct rtattr *rta;
+ struct zbuf rtapl, pktpl;
+ struct interface *ifp;
+ struct nfulnl_msg_packet_hdr *pkthdr = NULL;
+ uint32_t *in_ndx = NULL;
+
+ nf = znl_pull(zb, sizeof(*nf));
+ if (!nf) return;
+
+ memset(&pktpl, 0, sizeof(pktpl));
+ while ((rta = znl_rta_pull(zb, &rtapl)) != NULL) {
+ switch (rta->rta_type) {
+ case NFULA_PACKET_HDR:
+ pkthdr = znl_pull(&rtapl, sizeof(*pkthdr));
+ break;
+ case NFULA_IFINDEX_INDEV:
+ in_ndx = znl_pull(&rtapl, sizeof(*in_ndx));
+ break;
+ case NFULA_PAYLOAD:
+ pktpl = rtapl;
+ break;
+ /* NFULA_HWHDR exists and is supposed to contain source
+ * hardware address. However, for ip_gre it seems to be
+ * the nexthop destination address if the packet matches
+ * route. */
+ }
+ }
+
+ if (!pkthdr || !in_ndx || !zbuf_used(&pktpl))
+ return;
+
+ ifp = if_lookup_by_index(htonl(*in_ndx));
+ if (!ifp)
+ return;
+
+ nhrp_peer_send_indication(ifp, htons(pkthdr->hw_protocol), &pktpl);
+}
+
+static int netlink_log_recv(struct thread *t)
+{
+ uint8_t buf[ZNL_BUFFER_SIZE];
+ int fd = THREAD_FD(t);
+ struct zbuf payload, zb;
+ struct nlmsghdr *n;
+
+ netlink_log_thread = NULL;
+
+ zbuf_init(&zb, buf, sizeof(buf), 0);
+ while (zbuf_recv(&zb, fd) > 0) {
+ while ((n = znl_nlmsg_pull(&zb, &payload)) != 0) {
+ debugf(NHRP_DEBUG_KERNEL, "Netlink-log: Received msg_type %u, msg_flags %u",
+ n->nlmsg_type, n->nlmsg_flags);
+ switch (n->nlmsg_type) {
+ case (NFNL_SUBSYS_ULOG<<8) | NFULNL_MSG_PACKET:
+ netlink_log_indication(n, &payload);
+ break;
+ }
+ }
+ }
+
+ THREAD_READ_ON(master, netlink_log_thread, netlink_log_recv, 0, netlink_log_fd);
+
+ return 0;
+}
+
+void netlink_set_nflog_group(int nlgroup)
+{
+ if (netlink_log_fd >= 0) {
+ THREAD_OFF(netlink_log_thread);
+ close(netlink_log_fd);
+ netlink_log_fd = -1;
+ }
+ netlink_nflog_group = nlgroup;
+ if (nlgroup) {
+ netlink_log_fd = znl_open(NETLINK_NETFILTER, 0);
+ netlink_log_register(netlink_log_fd, nlgroup);
+ THREAD_READ_ON(master, netlink_log_thread, netlink_log_recv, 0, netlink_log_fd);
+ }
+}
+
+int netlink_init(void)
+{
+ netlink_req_fd = znl_open(NETLINK_ROUTE, 0);
+ netlink_listen_fd = znl_open(NETLINK_ROUTE, RTMGRP_NEIGH);
+ thread_add_read(master, netlink_route_recv, 0, netlink_listen_fd);
+
+ return 0;
+}
+
+int netlink_configure_arp(unsigned int ifindex, int pf)
+{
+ struct nlmsghdr *n;
+ struct ndtmsg *ndtm;
+ struct rtattr *rta;
+ struct zbuf *zb = zbuf_alloc(512);
+ int r;
+
+ n = znl_nlmsg_push(zb, RTM_SETNEIGHTBL, NLM_F_REQUEST | NLM_F_REPLACE);
+ ndtm = znl_push(zb, sizeof(*ndtm));
+ *ndtm = (struct ndtmsg) {
+ .ndtm_family = pf,
+ };
+
+ znl_rta_push(zb, NDTA_NAME, pf == AF_INET ? "arp_cache" : "ndisc_cache", 10);
+
+ rta = znl_rta_nested_push(zb, NDTA_PARMS);
+ znl_rta_push_u32(zb, NDTPA_IFINDEX, ifindex);
+ znl_rta_push_u32(zb, NDTPA_APP_PROBES, 1);
+ znl_rta_push_u32(zb, NDTPA_MCAST_PROBES, 0);
+ znl_rta_push_u32(zb, NDTPA_UCAST_PROBES, 0);
+ znl_rta_nested_complete(zb, rta);
+
+ znl_nlmsg_complete(zb, n);
+ r = zbuf_send(zb, netlink_req_fd);
+ zbuf_recv(zb, netlink_req_fd);
+ zbuf_free(zb);
+
+ return r;
+}
diff --git a/nhrpd/netlink_gre.c b/nhrpd/netlink_gre.c
new file mode 100644
index 0000000000..93998dc5f5
--- /dev/null
+++ b/nhrpd/netlink_gre.c
@@ -0,0 +1,142 @@
+/* NHRP netlink/GRE tunnel configuration code
+ * Copyright (c) 2014-2016 Timo Teräs
+ *
+ * This file is free software: you may copy, redistribute 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.
+ */
+
+#include <sys/socket.h>
+#include <linux/netlink.h>
+#include <linux/rtnetlink.h>
+#include <linux/in.h>
+#include <linux/if.h>
+#include <linux/ip.h>
+#include <linux/ipv6.h>
+#include <linux/if_tunnel.h>
+
+#include "debug.h"
+#include "netlink.h"
+#include "znl.h"
+
+static int __netlink_gre_get_data(struct zbuf *zb, struct zbuf *data, int ifindex)
+{
+ struct nlmsghdr *n;
+ struct ifinfomsg *ifi;
+ struct zbuf payload, rtapayload;
+ struct rtattr *rta;
+
+ debugf(NHRP_DEBUG_KERNEL, "netlink-link-gre: get-info %u", ifindex);
+
+ n = znl_nlmsg_push(zb, RTM_GETLINK, NLM_F_REQUEST);
+ ifi = znl_push(zb, sizeof(*ifi));
+ *ifi = (struct ifinfomsg) {
+ .ifi_index = ifindex,
+ };
+ znl_nlmsg_complete(zb, n);
+
+ if (zbuf_send(zb, netlink_req_fd) < 0 ||
+ zbuf_recv(zb, netlink_req_fd) < 0)
+ return -1;
+
+ n = znl_nlmsg_pull(zb, &payload);
+ if (!n) return -1;
+
+ if (n->nlmsg_type != RTM_NEWLINK)
+ return -1;
+
+ ifi = znl_pull(&payload, sizeof(struct ifinfomsg));
+ if (!ifi)
+ return -1;
+
+ debugf(NHRP_DEBUG_KERNEL, "netlink-link-gre: ifindex %u, receive msg_type %u, msg_flags %u",
+ ifi->ifi_index, n->nlmsg_type, n->nlmsg_flags);
+
+ if (ifi->ifi_index != ifindex)
+ return -1;
+
+ while ((rta = znl_rta_pull(&payload, &rtapayload)) != NULL)
+ if (rta->rta_type == IFLA_LINKINFO)
+ break;
+ if (!rta) return -1;
+
+ payload = rtapayload;
+ while ((rta = znl_rta_pull(&payload, &rtapayload)) != NULL)
+ if (rta->rta_type == IFLA_INFO_DATA)
+ break;
+ if (!rta) return -1;
+
+ *data = rtapayload;
+ return 0;
+}
+
+void netlink_gre_get_info(unsigned int ifindex, uint32_t *gre_key, unsigned int *link_index, struct in_addr *saddr)
+{
+ struct zbuf *zb = zbuf_alloc(8192), data, rtapl;
+ struct rtattr *rta;
+
+ *link_index = 0;
+ *gre_key = 0;
+ saddr->s_addr = 0;
+
+ if (__netlink_gre_get_data(zb, &data, ifindex) < 0)
+ goto err;
+
+ while ((rta = znl_rta_pull(&data, &rtapl)) != NULL) {
+ switch (rta->rta_type) {
+ case IFLA_GRE_LINK:
+ *link_index = zbuf_get32(&rtapl);
+ break;
+ case IFLA_GRE_IKEY:
+ case IFLA_GRE_OKEY:
+ *gre_key = zbuf_get32(&rtapl);
+ break;
+ case IFLA_GRE_LOCAL:
+ saddr->s_addr = zbuf_get32(&rtapl);
+ break;
+ }
+ }
+err:
+ zbuf_free(zb);
+}
+
+void netlink_gre_set_link(unsigned int ifindex, unsigned int link_index)
+{
+ struct nlmsghdr *n;
+ struct ifinfomsg *ifi;
+ struct rtattr *rta_info, *rta_data, *rta;
+ struct zbuf *zr = zbuf_alloc(8192), data, rtapl;
+ struct zbuf *zb = zbuf_alloc(8192);
+ size_t len;
+
+ if (__netlink_gre_get_data(zr, &data, ifindex) < 0)
+ goto err;
+
+ n = znl_nlmsg_push(zb, RTM_NEWLINK, NLM_F_REQUEST);
+ ifi = znl_push(zb, sizeof(*ifi));
+ *ifi = (struct ifinfomsg) {
+ .ifi_index = ifindex,
+ };
+ rta_info = znl_rta_nested_push(zb, IFLA_LINKINFO);
+ znl_rta_push(zb, IFLA_INFO_KIND, "gre", 3);
+ rta_data = znl_rta_nested_push(zb, IFLA_INFO_DATA);
+
+ znl_rta_push_u32(zb, IFLA_GRE_LINK, link_index);
+ while ((rta = znl_rta_pull(&data, &rtapl)) != NULL) {
+ if (rta->rta_type == IFLA_GRE_LINK)
+ continue;
+ len = zbuf_used(&rtapl);
+ znl_rta_push(zb, rta->rta_type, zbuf_pulln(&rtapl, len), len);
+ }
+
+ znl_rta_nested_complete(zb, rta_data);
+ znl_rta_nested_complete(zb, rta_info);
+
+ znl_nlmsg_complete(zb, n);
+ zbuf_send(zb, netlink_req_fd);
+ zbuf_recv(zb, netlink_req_fd);
+err:
+ zbuf_free(zb);
+ zbuf_free(zr);
+}
diff --git a/nhrpd/nhrp_cache.c b/nhrpd/nhrp_cache.c
new file mode 100644
index 0000000000..d9094ec335
--- /dev/null
+++ b/nhrpd/nhrp_cache.c
@@ -0,0 +1,343 @@
+/* NHRP cache
+ * Copyright (c) 2014-2015 Timo Teräs
+ *
+ * This file is free software: you may copy, redistribute 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.
+ */
+
+#include "zebra.h"
+#include "memory.h"
+#include "thread.h"
+#include "hash.h"
+#include "nhrpd.h"
+
+#include "netlink.h"
+
+DEFINE_MTYPE_STATIC(NHRPD, NHRP_CACHE, "NHRP cache entry")
+
+unsigned long nhrp_cache_counts[NHRP_CACHE_NUM_TYPES];
+
+const char * const nhrp_cache_type_str[] = {
+ [NHRP_CACHE_INVALID] = "invalid",
+ [NHRP_CACHE_INCOMPLETE] = "incomplete",
+ [NHRP_CACHE_NEGATIVE] = "negative",
+ [NHRP_CACHE_CACHED] = "cached",
+ [NHRP_CACHE_DYNAMIC] = "dynamic",
+ [NHRP_CACHE_NHS] = "nhs",
+ [NHRP_CACHE_STATIC] = "static",
+ [NHRP_CACHE_LOCAL] = "local",
+};
+
+static unsigned int nhrp_cache_protocol_key(void *peer_data)
+{
+ struct nhrp_cache *p = peer_data;
+ return sockunion_hash(&p->remote_addr);
+}
+
+static int nhrp_cache_protocol_cmp(const void *cache_data, const void *key_data)
+{
+ const struct nhrp_cache *a = cache_data;
+ const struct nhrp_cache *b = key_data;
+ return sockunion_same(&a->remote_addr, &b->remote_addr);
+}
+
+static void *nhrp_cache_alloc(void *data)
+{
+ struct nhrp_cache *p, *key = data;
+
+ p = XMALLOC(MTYPE_NHRP_CACHE, sizeof(struct nhrp_cache));
+ if (p) {
+ *p = (struct nhrp_cache) {
+ .cur.type = NHRP_CACHE_INVALID,
+ .new.type = NHRP_CACHE_INVALID,
+ .remote_addr = key->remote_addr,
+ .ifp = key->ifp,
+ .notifier_list = NOTIFIER_LIST_INITIALIZER(&p->notifier_list),
+ };
+ nhrp_cache_counts[p->cur.type]++;
+ }
+
+ return p;
+}
+
+static void nhrp_cache_free(struct nhrp_cache *c)
+{
+ struct nhrp_interface *nifp = c->ifp->info;
+
+ zassert(c->cur.type == NHRP_CACHE_INVALID && c->cur.peer == NULL);
+ zassert(c->new.type == NHRP_CACHE_INVALID && c->new.peer == NULL);
+ nhrp_cache_counts[c->cur.type]--;
+ notifier_call(&c->notifier_list, NOTIFY_CACHE_DELETE);
+ zassert(!notifier_active(&c->notifier_list));
+ hash_release(nifp->cache_hash, c);
+ XFREE(MTYPE_NHRP_CACHE, c);
+}
+
+struct nhrp_cache *nhrp_cache_get(struct interface *ifp, union sockunion *remote_addr, int create)
+{
+ struct nhrp_interface *nifp = ifp->info;
+ struct nhrp_cache key;
+
+ if (!nifp->cache_hash) {
+ nifp->cache_hash = hash_create(nhrp_cache_protocol_key, nhrp_cache_protocol_cmp);
+ if (!nifp->cache_hash)
+ return NULL;
+ }
+
+ key.remote_addr = *remote_addr;
+ key.ifp = ifp;
+
+ return hash_get(nifp->cache_hash, &key, create ? nhrp_cache_alloc : NULL);
+}
+
+static int nhrp_cache_do_free(struct thread *t)
+{
+ struct nhrp_cache *c = THREAD_ARG(t);
+ c->t_timeout = NULL;
+ nhrp_cache_free(c);
+ return 0;
+}
+
+static int nhrp_cache_do_timeout(struct thread *t)
+{
+ struct nhrp_cache *c = THREAD_ARG(t);
+ c->t_timeout = NULL;
+ if (c->cur.type != NHRP_CACHE_INVALID)
+ nhrp_cache_update_binding(c, c->cur.type, -1, NULL, 0, NULL);
+ return 0;
+}
+
+static void nhrp_cache_update_route(struct nhrp_cache *c)
+{
+ struct prefix pfx;
+ struct nhrp_peer *p = c->cur.peer;
+
+ sockunion2hostprefix(&c->remote_addr, &pfx);
+
+ if (p && nhrp_peer_check(p, 1)) {
+ netlink_update_binding(p->ifp, &c->remote_addr, &p->vc->remote.nbma);
+ nhrp_route_announce(1, c->cur.type, &pfx, c->ifp, NULL, c->cur.mtu);
+ if (c->cur.type >= NHRP_CACHE_DYNAMIC) {
+ nhrp_route_update_nhrp(&pfx, c->ifp);
+ c->nhrp_route_installed = 1;
+ } else if (c->nhrp_route_installed) {
+ nhrp_route_update_nhrp(&pfx, NULL);
+ c->nhrp_route_installed = 0;
+ }
+ if (!c->route_installed) {
+ notifier_call(&c->notifier_list, NOTIFY_CACHE_UP);
+ c->route_installed = 1;
+ }
+ } else {
+ if (c->nhrp_route_installed) {
+ nhrp_route_update_nhrp(&pfx, NULL);
+ c->nhrp_route_installed = 0;
+ }
+ if (c->route_installed) {
+ sockunion2hostprefix(&c->remote_addr, &pfx);
+ notifier_call(&c->notifier_list, NOTIFY_CACHE_DOWN);
+ nhrp_route_announce(0, c->cur.type, &pfx, NULL, NULL, 0);
+ c->route_installed = 0;
+ }
+ }
+}
+
+static void nhrp_cache_peer_notifier(struct notifier_block *n, unsigned long cmd)
+{
+ struct nhrp_cache *c = container_of(n, struct nhrp_cache, peer_notifier);
+
+ switch (cmd) {
+ case NOTIFY_PEER_UP:
+ nhrp_cache_update_route(c);
+ break;
+ case NOTIFY_PEER_DOWN:
+ case NOTIFY_PEER_IFCONFIG_CHANGED:
+ notifier_call(&c->notifier_list, NOTIFY_CACHE_DOWN);
+ nhrp_cache_update_binding(c, c->cur.type, -1, NULL, 0, NULL);
+ break;
+ case NOTIFY_PEER_NBMA_CHANGING:
+ if (c->cur.type == NHRP_CACHE_DYNAMIC)
+ c->cur.peer->vc->abort_migration = 1;
+ break;
+ }
+}
+
+static void nhrp_cache_reset_new(struct nhrp_cache *c)
+{
+ THREAD_OFF(c->t_auth);
+ if (list_hashed(&c->newpeer_notifier.notifier_entry))
+ nhrp_peer_notify_del(c->new.peer, &c->newpeer_notifier);
+ nhrp_peer_unref(c->new.peer);
+ memset(&c->new, 0, sizeof(c->new));
+ c->new.type = NHRP_CACHE_INVALID;
+}
+
+static void nhrp_cache_update_timers(struct nhrp_cache *c)
+{
+ THREAD_OFF(c->t_timeout);
+
+ switch (c->cur.type) {
+ case NHRP_CACHE_INVALID:
+ if (!c->t_auth)
+ THREAD_TIMER_MSEC_ON(master, c->t_timeout, nhrp_cache_do_free, c, 10);
+ break;
+ default:
+ if (c->cur.expires)
+ THREAD_TIMER_ON(master, c->t_timeout, nhrp_cache_do_timeout, c, c->cur.expires - monotime(NULL));
+ break;
+ }
+}
+
+static void nhrp_cache_authorize_binding(struct nhrp_reqid *r, void *arg)
+{
+ struct nhrp_cache *c = container_of(r, struct nhrp_cache, eventid);
+ char buf[SU_ADDRSTRLEN];
+
+ debugf(NHRP_DEBUG_COMMON, "cache: %s %s: %s",
+ c->ifp->name, sockunion2str(&c->remote_addr, buf, sizeof buf),
+ (const char *) arg);
+
+ nhrp_reqid_free(&nhrp_event_reqid, r);
+
+ if (arg && strcmp(arg, "accept") == 0) {
+ if (c->cur.peer) {
+ netlink_update_binding(c->cur.peer->ifp, &c->remote_addr, NULL);
+ nhrp_peer_notify_del(c->cur.peer, &c->peer_notifier);
+ nhrp_peer_unref(c->cur.peer);
+ }
+ nhrp_cache_counts[c->cur.type]--;
+ nhrp_cache_counts[c->new.type]++;
+ c->cur = c->new;
+ c->cur.peer = nhrp_peer_ref(c->cur.peer);
+ nhrp_cache_reset_new(c);
+ if (c->cur.peer)
+ nhrp_peer_notify_add(c->cur.peer, &c->peer_notifier, nhrp_cache_peer_notifier);
+ nhrp_cache_update_route(c);
+ notifier_call(&c->notifier_list, NOTIFY_CACHE_BINDING_CHANGE);
+ } else {
+ nhrp_cache_reset_new(c);
+ }
+
+ nhrp_cache_update_timers(c);
+}
+
+static int nhrp_cache_do_auth_timeout(struct thread *t)
+{
+ struct nhrp_cache *c = THREAD_ARG(t);
+ c->t_auth = NULL;
+ nhrp_cache_authorize_binding(&c->eventid, (void *) "timeout");
+ return 0;
+}
+
+static void nhrp_cache_newpeer_notifier(struct notifier_block *n, unsigned long cmd)
+{
+ struct nhrp_cache *c = container_of(n, struct nhrp_cache, newpeer_notifier);
+
+ switch (cmd) {
+ case NOTIFY_PEER_UP:
+ if (nhrp_peer_check(c->new.peer, 1)) {
+ evmgr_notify("authorize-binding", c, nhrp_cache_authorize_binding);
+ THREAD_TIMER_ON(master, c->t_auth, nhrp_cache_do_auth_timeout, c, 10);
+ }
+ break;
+ case NOTIFY_PEER_DOWN:
+ case NOTIFY_PEER_IFCONFIG_CHANGED:
+ nhrp_cache_reset_new(c);
+ break;
+ }
+}
+
+int nhrp_cache_update_binding(struct nhrp_cache *c, enum nhrp_cache_type type, int holding_time, struct nhrp_peer *p, uint32_t mtu, union sockunion *nbma_oa)
+{
+ if (c->cur.type > type || c->new.type > type) {
+ nhrp_peer_unref(p);
+ return 0;
+ }
+
+ /* Sanitize MTU */
+ switch (sockunion_family(&c->remote_addr)) {
+ case AF_INET:
+ if (mtu < 576 || mtu >= 1500)
+ mtu = 0;
+ /* Opennhrp announces nbma mtu, but we use protocol mtu.
+ * This heuristic tries to fix up it. */
+ if (mtu > 1420) mtu = (mtu & -16) - 80;
+ break;
+ default:
+ mtu = 0;
+ break;
+ }
+
+ nhrp_cache_reset_new(c);
+ if (c->cur.type == type && c->cur.peer == p && c->cur.mtu == mtu) {
+ if (holding_time > 0) c->cur.expires = monotime(NULL) + holding_time;
+ if (nbma_oa) c->cur.remote_nbma_natoa = *nbma_oa;
+ else memset(&c->cur.remote_nbma_natoa, 0, sizeof c->cur.remote_nbma_natoa);
+ nhrp_peer_unref(p);
+ } else {
+ c->new.type = type;
+ c->new.peer = p;
+ c->new.mtu = mtu;
+ if (nbma_oa) c->new.remote_nbma_natoa = *nbma_oa;
+
+ if (holding_time > 0)
+ c->new.expires = monotime(NULL) + holding_time;
+ else if (holding_time < 0)
+ c->new.type = NHRP_CACHE_INVALID;
+
+ if (c->new.type == NHRP_CACHE_INVALID ||
+ c->new.type >= NHRP_CACHE_STATIC ||
+ c->map) {
+ nhrp_cache_authorize_binding(&c->eventid, (void *) "accept");
+ } else {
+ nhrp_peer_notify_add(c->new.peer, &c->newpeer_notifier, nhrp_cache_newpeer_notifier);
+ nhrp_cache_newpeer_notifier(&c->newpeer_notifier, NOTIFY_PEER_UP);
+ THREAD_TIMER_ON(master, c->t_auth, nhrp_cache_do_auth_timeout, c, 60);
+ }
+ }
+ nhrp_cache_update_timers(c);
+
+ return 1;
+}
+
+void nhrp_cache_set_used(struct nhrp_cache *c, int used)
+{
+ c->used = used;
+ if (c->used)
+ notifier_call(&c->notifier_list, NOTIFY_CACHE_USED);
+}
+
+struct nhrp_cache_iterator_ctx {
+ void (*cb)(struct nhrp_cache *, void *);
+ void *ctx;
+};
+
+static void nhrp_cache_iterator(struct hash_backet *b, void *ctx)
+{
+ struct nhrp_cache_iterator_ctx *ic = ctx;
+ ic->cb(b->data, ic->ctx);
+}
+
+void nhrp_cache_foreach(struct interface *ifp, void (*cb)(struct nhrp_cache *, void *), void *ctx)
+{
+ struct nhrp_interface *nifp = ifp->info;
+ struct nhrp_cache_iterator_ctx ic = {
+ .cb = cb,
+ .ctx = ctx,
+ };
+
+ if (nifp->cache_hash)
+ hash_iterate(nifp->cache_hash, nhrp_cache_iterator, &ic);
+}
+
+void nhrp_cache_notify_add(struct nhrp_cache *c, struct notifier_block *n, notifier_fn_t fn)
+{
+ notifier_add(n, &c->notifier_list, fn);
+}
+
+void nhrp_cache_notify_del(struct nhrp_cache *c, struct notifier_block *n)
+{
+ notifier_del(n);
+}
diff --git a/nhrpd/nhrp_event.c b/nhrpd/nhrp_event.c
new file mode 100644
index 0000000000..aab9ec642f
--- /dev/null
+++ b/nhrpd/nhrp_event.c
@@ -0,0 +1,280 @@
+/* NHRP event manager
+ * Copyright (c) 2014-2015 Timo Teräs
+ *
+ * This file is free software: you may copy, redistribute 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.
+ */
+
+#include <string.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+
+#include "thread.h"
+#include "zbuf.h"
+#include "log.h"
+#include "nhrpd.h"
+
+const char *nhrp_event_socket_path;
+struct nhrp_reqid_pool nhrp_event_reqid;
+
+struct event_manager {
+ struct thread *t_reconnect, *t_read, *t_write;
+ struct zbuf ibuf;
+ struct zbuf_queue obuf;
+ int fd;
+ uint8_t ibuf_data[4*1024];
+};
+
+static int evmgr_reconnect(struct thread *t);
+
+static void evmgr_connection_error(struct event_manager *evmgr)
+{
+ THREAD_OFF(evmgr->t_read);
+ THREAD_OFF(evmgr->t_write);
+ zbuf_reset(&evmgr->ibuf);
+ zbufq_reset(&evmgr->obuf);
+
+ if (evmgr->fd >= 0)
+ close(evmgr->fd);
+ evmgr->fd = -1;
+ if (nhrp_event_socket_path)
+ THREAD_TIMER_MSEC_ON(master, evmgr->t_reconnect, evmgr_reconnect,
+ evmgr, 10);
+}
+
+static void evmgr_recv_message(struct event_manager *evmgr, struct zbuf *zb)
+{
+ struct zbuf zl;
+ uint32_t eventid = 0;
+ size_t len;
+ char buf[256], result[64] = "";
+
+ while (zbuf_may_pull_until(zb, "\n", &zl)) {
+ len = zbuf_used(&zl) - 1;
+ if (len >= sizeof(buf)-1)
+ continue;
+ memcpy(buf, zbuf_pulln(&zl, len), len);
+ buf[len] = 0;
+
+ debugf(NHRP_DEBUG_EVENT, "evmgr: msg: %s", buf);
+ sscanf(buf, "eventid=%d", &eventid);
+ sscanf(buf, "result=%63s", result);
+ }
+ debugf(NHRP_DEBUG_EVENT, "evmgr: received: eventid=%d result=%s", eventid, result);
+ if (eventid && result[0]) {
+ struct nhrp_reqid *r = nhrp_reqid_lookup(&nhrp_event_reqid, eventid);
+ if (r) r->cb(r, result);
+ }
+}
+
+static int evmgr_read(struct thread *t)
+{
+ struct event_manager *evmgr = THREAD_ARG(t);
+ struct zbuf *ibuf = &evmgr->ibuf;
+ struct zbuf msg;
+
+ evmgr->t_read = NULL;
+ if (zbuf_read(ibuf, evmgr->fd, (size_t) -1) < 0) {
+ evmgr_connection_error(evmgr);
+ return 0;
+ }
+
+ /* Process all messages in buffer */
+ while (zbuf_may_pull_until(ibuf, "\n\n", &msg))
+ evmgr_recv_message(evmgr, &msg);
+
+ THREAD_READ_ON(master, evmgr->t_read, evmgr_read, evmgr, evmgr->fd);
+ return 0;
+}
+
+static int evmgr_write(struct thread *t)
+{
+ struct event_manager *evmgr = THREAD_ARG(t);
+ int r;
+
+ evmgr->t_write = NULL;
+ r = zbufq_write(&evmgr->obuf, evmgr->fd);
+ if (r > 0) {
+ THREAD_WRITE_ON(master, evmgr->t_write, evmgr_write, evmgr, evmgr->fd);
+ } else if (r < 0) {
+ evmgr_connection_error(evmgr);
+ }
+
+ return 0;
+}
+
+static void evmgr_hexdump(struct zbuf *zb, const uint8_t *val, size_t vallen)
+{
+ static const char xd[] = "0123456789abcdef";
+ size_t i;
+ char *ptr;
+
+ ptr = zbuf_pushn(zb, 2*vallen);
+ if (!ptr) return;
+
+ for (i = 0; i < vallen; i++) {
+ uint8_t b = val[i];
+ *(ptr++) = xd[b >> 4];
+ *(ptr++) = xd[b & 0xf];
+ }
+}
+
+static void evmgr_put(struct zbuf *zb, const char *fmt, ...)
+{
+ const char *pos, *nxt, *str;
+ const uint8_t *bin;
+ const union sockunion *su;
+ int len;
+ va_list va;
+
+ va_start(va, fmt);
+ for (pos = fmt; (nxt = strchr(pos, '%')) != NULL; pos = nxt + 2) {
+ zbuf_put(zb, pos, nxt-pos);
+ switch (nxt[1]) {
+ case '%':
+ zbuf_put8(zb, '%');
+ break;
+ case 'u':
+ zb->tail += snprintf((char *) zb->tail, zbuf_tailroom(zb), "%u", va_arg(va, uint32_t));
+ break;
+ case 's':
+ str = va_arg(va, const char *);
+ zbuf_put(zb, str, strlen(str));
+ break;
+ case 'U':
+ su = va_arg(va, const union sockunion *);
+ if (sockunion2str(su, (char *) zb->tail, zbuf_tailroom(zb)))
+ zb->tail += strlen((char *) zb->tail);
+ else
+ zbuf_set_werror(zb);
+ break;
+ case 'H':
+ bin = va_arg(va, const uint8_t *);
+ len = va_arg(va, int);
+ evmgr_hexdump(zb, bin, len);
+ break;
+ }
+ }
+ va_end(va);
+ zbuf_put(zb, pos, strlen(pos));
+}
+
+static void evmgr_submit(struct event_manager *evmgr, struct zbuf *obuf)
+{
+ if (obuf->error) {
+ zbuf_free(obuf);
+ return;
+ }
+ zbuf_put(obuf, "\n", 1);
+ zbufq_queue(&evmgr->obuf, obuf);
+ if (evmgr->fd >= 0)
+ THREAD_WRITE_ON(master, evmgr->t_write, evmgr_write, evmgr, evmgr->fd);
+}
+
+static int evmgr_reconnect(struct thread *t)
+{
+ struct event_manager *evmgr = THREAD_ARG(t);
+ int fd;
+
+ evmgr->t_reconnect = NULL;
+ if (evmgr->fd >= 0 || !nhrp_event_socket_path) return 0;
+
+ fd = sock_open_unix(nhrp_event_socket_path);
+ if (fd < 0) {
+ zlog_warn("%s: failure connecting nhrp-event socket: %s",
+ __PRETTY_FUNCTION__, strerror(errno));
+ zbufq_reset(&evmgr->obuf);
+ THREAD_TIMER_ON(master, evmgr->t_reconnect, evmgr_reconnect, evmgr, 10);
+ return 0;
+ }
+
+ zlog_info("Connected to Event Manager");
+ evmgr->fd = fd;
+ THREAD_READ_ON(master, evmgr->t_read, evmgr_read, evmgr, evmgr->fd);
+
+ return 0;
+}
+
+static struct event_manager evmgr_connection;
+
+void evmgr_init(void)
+{
+ struct event_manager *evmgr = &evmgr_connection;
+
+ evmgr->fd = -1;
+ zbuf_init(&evmgr->ibuf, evmgr->ibuf_data, sizeof(evmgr->ibuf_data), 0);
+ zbufq_init(&evmgr->obuf);
+ THREAD_TIMER_MSEC_ON(master, evmgr->t_reconnect, evmgr_reconnect, evmgr, 10);
+}
+
+void evmgr_set_socket(const char *socket)
+{
+ if (nhrp_event_socket_path)
+ free((char *) nhrp_event_socket_path);
+ nhrp_event_socket_path = strdup(socket);
+ evmgr_connection_error(&evmgr_connection);
+}
+
+void evmgr_terminate(void)
+{
+}
+
+void evmgr_notify(const char *name, struct nhrp_cache *c, void (*cb)(struct nhrp_reqid *, void *))
+{
+ struct event_manager *evmgr = &evmgr_connection;
+ struct nhrp_vc *vc;
+ struct nhrp_interface *nifp = c->ifp->info;
+ struct zbuf *zb;
+ afi_t afi = family2afi(sockunion_family(&c->remote_addr));
+
+ if (!nhrp_event_socket_path) {
+ cb(&c->eventid, (void*) "accept");
+ return;
+ }
+
+ debugf(NHRP_DEBUG_EVENT, "evmgr: sending event %s", name);
+
+ vc = c->new.peer ? c->new.peer->vc : NULL;
+ zb = zbuf_alloc(1024 + (vc ? (vc->local.certlen + vc->remote.certlen) * 2 : 0));
+
+ if (cb) {
+ nhrp_reqid_free(&nhrp_event_reqid, &c->eventid);
+ evmgr_put(zb,
+ "eventid=%u\n",
+ nhrp_reqid_alloc(&nhrp_event_reqid, &c->eventid, cb));
+ }
+
+ evmgr_put(zb,
+ "event=%s\n"
+ "type=%s\n"
+ "old_type=%s\n"
+ "num_nhs=%u\n"
+ "interface=%s\n"
+ "local_addr=%U\n",
+ name,
+ nhrp_cache_type_str[c->new.type],
+ nhrp_cache_type_str[c->cur.type],
+ (unsigned int) nhrp_cache_counts[NHRP_CACHE_NHS],
+ c->ifp->name,
+ &nifp->afi[afi].addr);
+
+ if (vc) {
+ evmgr_put(zb,
+ "vc_initiated=%s\n"
+ "local_nbma=%U\n"
+ "local_cert=%H\n"
+ "remote_addr=%U\n"
+ "remote_nbma=%U\n"
+ "remote_cert=%H\n",
+ c->new.peer->requested ? "yes" : "no",
+ &vc->local.nbma,
+ vc->local.cert, vc->local.certlen,
+ &c->remote_addr, &vc->remote.nbma,
+ vc->remote.cert, vc->remote.certlen);
+ }
+
+ evmgr_submit(evmgr, zb);
+}
+
diff --git a/nhrpd/nhrp_interface.c b/nhrpd/nhrp_interface.c
new file mode 100644
index 0000000000..4ac612d3ad
--- /dev/null
+++ b/nhrpd/nhrp_interface.c
@@ -0,0 +1,406 @@
+/* NHRP interface
+ * Copyright (c) 2014-2015 Timo Teräs
+ *
+ * This file is free software: you may copy, redistribute 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.
+ */
+
+#include <net/if_arp.h>
+#include "zebra.h"
+#include "linklist.h"
+#include "memory.h"
+#include "thread.h"
+
+#include "nhrpd.h"
+#include "os.h"
+#include "netlink.h"
+
+DEFINE_MTYPE_STATIC(NHRPD, NHRP_IF, "NHRP interface")
+
+static int nhrp_if_new_hook(struct interface *ifp)
+{
+ struct nhrp_interface *nifp;
+ afi_t afi;
+
+ nifp = XCALLOC(MTYPE_NHRP_IF, sizeof(struct nhrp_interface));
+ if (!nifp) return 0;
+
+ ifp->info = nifp;
+ nifp->ifp = ifp;
+
+ notifier_init(&nifp->notifier_list);
+ for (afi = 0; afi < AFI_MAX; afi++) {
+ struct nhrp_afi_data *ad = &nifp->afi[afi];
+ ad->holdtime = NHRPD_DEFAULT_HOLDTIME;
+ list_init(&ad->nhslist_head);
+ }
+
+ return 0;
+}
+
+static int nhrp_if_delete_hook(struct interface *ifp)
+{
+ XFREE(MTYPE_NHRP_IF, ifp->info);
+ return 0;
+}
+
+void nhrp_interface_init(void)
+{
+ if_add_hook(IF_NEW_HOOK, nhrp_if_new_hook);
+ if_add_hook(IF_DELETE_HOOK, nhrp_if_delete_hook);
+}
+
+void nhrp_interface_update_mtu(struct interface *ifp, afi_t afi)
+{
+ struct nhrp_interface *nifp = ifp->info;
+ struct nhrp_afi_data *if_ad = &nifp->afi[afi];
+ unsigned short new_mtu;
+
+ if (if_ad->configured_mtu < 0)
+ new_mtu = nifp->nbmaifp ? nifp->nbmaifp->mtu : 0;
+ else
+ new_mtu = if_ad->configured_mtu;
+ if (new_mtu >= 1500)
+ new_mtu = 0;
+
+ if (new_mtu != if_ad->mtu) {
+ debugf(NHRP_DEBUG_IF, "%s: MTU changed to %d", ifp->name, new_mtu);
+ if_ad->mtu = new_mtu;
+ notifier_call(&nifp->notifier_list, NOTIFY_INTERFACE_MTU_CHANGED);
+ }
+}
+
+static void nhrp_interface_update_source(struct interface *ifp)
+{
+ struct nhrp_interface *nifp = ifp->info;
+
+ if (!nifp->source || !nifp->nbmaifp ||
+ (ifindex_t)nifp->linkidx == nifp->nbmaifp->ifindex)
+ return;
+
+ nifp->linkidx = nifp->nbmaifp->ifindex;
+ debugf(NHRP_DEBUG_IF, "%s: bound device index changed to %d", ifp->name, nifp->linkidx);
+ netlink_gre_set_link(ifp->ifindex, nifp->linkidx);
+}
+
+static void nhrp_interface_interface_notifier(struct notifier_block *n, unsigned long cmd)
+{
+ struct nhrp_interface *nifp = container_of(n, struct nhrp_interface, nbmanifp_notifier);
+ struct interface *nbmaifp = nifp->nbmaifp;
+ struct nhrp_interface *nbmanifp = nbmaifp->info;
+ char buf[SU_ADDRSTRLEN];
+
+ switch (cmd) {
+ case NOTIFY_INTERFACE_CHANGED:
+ nhrp_interface_update_mtu(nifp->ifp, AFI_IP);
+ nhrp_interface_update_source(nifp->ifp);
+ break;
+ case NOTIFY_INTERFACE_ADDRESS_CHANGED:
+ nifp->nbma = nbmanifp->afi[AFI_IP].addr;
+ nhrp_interface_update(nifp->ifp);
+ notifier_call(&nifp->notifier_list, NOTIFY_INTERFACE_NBMA_CHANGED);
+ debugf(NHRP_DEBUG_IF, "%s: NBMA change: address %s",
+ nifp->ifp->name,
+ sockunion2str(&nifp->nbma, buf, sizeof buf));
+ break;
+ }
+}
+
+static void nhrp_interface_update_nbma(struct interface *ifp)
+{
+ struct nhrp_interface *nifp = ifp->info, *nbmanifp = NULL;
+ struct interface *nbmaifp = NULL;
+ union sockunion nbma;
+
+ sockunion_family(&nbma) = AF_UNSPEC;
+
+ if (nifp->source)
+ nbmaifp = if_lookup_by_name(nifp->source);
+
+ switch (ifp->ll_type) {
+ case ZEBRA_LLT_IPGRE: {
+ struct in_addr saddr = {0};
+ netlink_gre_get_info(ifp->ifindex, &nifp->grekey, &nifp->linkidx, &saddr);
+ debugf(NHRP_DEBUG_IF, "%s: GRE: %x %x %x", ifp->name, nifp->grekey, nifp->linkidx, saddr.s_addr);
+ if (saddr.s_addr)
+ sockunion_set(&nbma, AF_INET, (u_char *) &saddr.s_addr, sizeof(saddr.s_addr));
+ else if (!nbmaifp && nifp->linkidx != IFINDEX_INTERNAL)
+ nbmaifp = if_lookup_by_index(nifp->linkidx);
+ }
+ break;
+ default:
+ break;
+ }
+
+ if (nbmaifp)
+ nbmanifp = nbmaifp->info;
+
+ if (nbmaifp != nifp->nbmaifp) {
+ if (nifp->nbmaifp)
+ notifier_del(&nifp->nbmanifp_notifier);
+ nifp->nbmaifp = nbmaifp;
+ if (nbmaifp) {
+ notifier_add(&nifp->nbmanifp_notifier, &nbmanifp->notifier_list, nhrp_interface_interface_notifier);
+ debugf(NHRP_DEBUG_IF, "%s: bound to %s", ifp->name, nbmaifp->name);
+ }
+ }
+
+ if (nbmaifp) {
+ if (sockunion_family(&nbma) == AF_UNSPEC)
+ nbma = nbmanifp->afi[AFI_IP].addr;
+ nhrp_interface_update_mtu(ifp, AFI_IP);
+ nhrp_interface_update_source(ifp);
+ }
+
+ if (!sockunion_same(&nbma, &nifp->nbma)) {
+ nifp->nbma = nbma;
+ nhrp_interface_update(nifp->ifp);
+ debugf(NHRP_DEBUG_IF, "%s: NBMA address changed", ifp->name);
+ notifier_call(&nifp->notifier_list, NOTIFY_INTERFACE_NBMA_CHANGED);
+ }
+
+ nhrp_interface_update(ifp);
+}
+
+static void nhrp_interface_update_address(struct interface *ifp, afi_t afi, int force)
+{
+ const int family = afi2family(afi);
+ struct nhrp_interface *nifp = ifp->info;
+ struct nhrp_afi_data *if_ad = &nifp->afi[afi];
+ struct nhrp_cache *nc;
+ struct connected *c, *best;
+ struct listnode *cnode;
+ union sockunion addr;
+ char buf[PREFIX_STRLEN];
+
+ /* Select new best match preferring primary address */
+ best = NULL;
+ for (ALL_LIST_ELEMENTS_RO(ifp->connected, cnode, c)) {
+ if (PREFIX_FAMILY(c->address) != family)
+ continue;
+ if (best == NULL) {
+ best = c;
+ continue;
+ }
+ if ((best->flags & ZEBRA_IFA_SECONDARY) && !(c->flags & ZEBRA_IFA_SECONDARY)) {
+ best = c;
+ continue;
+ }
+ if (!(best->flags & ZEBRA_IFA_SECONDARY) && (c->flags & ZEBRA_IFA_SECONDARY))
+ continue;
+ if (best->address->prefixlen > c->address->prefixlen) {
+ best = c;
+ continue;
+ }
+ if (best->address->prefixlen < c->address->prefixlen)
+ continue;
+ }
+
+ /* On NHRP interfaces a host prefix is required */
+ if (best && if_ad->configured && best->address->prefixlen != 8 * prefix_blen(best->address)) {
+ zlog_notice("%s: %s is not a host prefix", ifp->name,
+ prefix2str(best->address, buf, sizeof buf));
+ best = NULL;
+ }
+
+ /* Update address if it changed */
+ if (best)
+ prefix2sockunion(best->address, &addr);
+ else
+ memset(&addr, 0, sizeof(addr));
+
+ if (!force && sockunion_same(&if_ad->addr, &addr))
+ return;
+
+ if (sockunion_family(&if_ad->addr) != AF_UNSPEC) {
+ nc = nhrp_cache_get(ifp, &if_ad->addr, 0);
+ if (nc) nhrp_cache_update_binding(nc, NHRP_CACHE_LOCAL, -1, NULL, 0, NULL);
+ }
+
+ debugf(NHRP_DEBUG_KERNEL, "%s: IPv%d address changed to %s",
+ ifp->name, afi == AFI_IP ? 4 : 6,
+ best ? prefix2str(best->address, buf, sizeof buf) : "(none)");
+ if_ad->addr = addr;
+
+ if (if_ad->configured && sockunion_family(&if_ad->addr) != AF_UNSPEC) {
+ nc = nhrp_cache_get(ifp, &addr, 1);
+ if (nc) nhrp_cache_update_binding(nc, NHRP_CACHE_LOCAL, 0, NULL, 0, NULL);
+ }
+
+ notifier_call(&nifp->notifier_list, NOTIFY_INTERFACE_ADDRESS_CHANGED);
+}
+
+void nhrp_interface_update(struct interface *ifp)
+{
+ struct nhrp_interface *nifp = ifp->info;
+ struct nhrp_afi_data *if_ad;
+ afi_t afi;
+ int enabled = 0;
+
+ notifier_call(&nifp->notifier_list, NOTIFY_INTERFACE_CHANGED);
+
+ for (afi = 0; afi < AFI_MAX; afi++) {
+ if_ad = &nifp->afi[afi];
+
+ if (sockunion_family(&nifp->nbma) == AF_UNSPEC ||
+ ifp->ifindex == IFINDEX_INTERNAL || !if_is_up(ifp) ||
+ !if_ad->network_id) {
+ if (if_ad->configured) {
+ if_ad->configured = 0;
+ nhrp_interface_update_address(ifp, afi, 1);
+ }
+ continue;
+ }
+
+ if (!if_ad->configured) {
+ os_configure_dmvpn(ifp->ifindex, ifp->name, afi2family(afi));
+ if_ad->configured = 1;
+ nhrp_interface_update_address(ifp, afi, 1);
+ }
+
+ enabled = 1;
+ }
+
+ if (enabled != nifp->enabled) {
+ nifp->enabled = enabled;
+ notifier_call(&nifp->notifier_list, enabled ? NOTIFY_INTERFACE_UP : NOTIFY_INTERFACE_DOWN);
+ }
+}
+
+int nhrp_interface_add(int cmd, struct zclient *client, zebra_size_t length, vrf_id_t vrf_id)
+{
+ struct interface *ifp;
+
+ /* read and add the interface in the iflist. */
+ ifp = zebra_interface_add_read(client->ibuf, vrf_id);
+ if (ifp == NULL)
+ return 0;
+
+ debugf(NHRP_DEBUG_IF, "if-add: %s, ifindex: %u, hw_type: %d %s",
+ ifp->name, ifp->ifindex,
+ ifp->ll_type, if_link_type_str(ifp->ll_type));
+
+ nhrp_interface_update_nbma(ifp);
+
+ return 0;
+}
+
+int nhrp_interface_delete(int cmd, struct zclient *client,
+ zebra_size_t length, vrf_id_t vrf_id)
+{
+ struct interface *ifp;
+ struct stream *s;
+
+ s = client->ibuf;
+ ifp = zebra_interface_state_read(s, vrf_id);
+ if (ifp == NULL)
+ return 0;
+
+ debugf(NHRP_DEBUG_IF, "if-delete: %s", ifp->name);
+ ifp->ifindex = IFINDEX_INTERNAL;
+ nhrp_interface_update(ifp);
+ /* if_delete(ifp); */
+ return 0;
+}
+
+int nhrp_interface_up(int cmd, struct zclient *client,
+ zebra_size_t length, vrf_id_t vrf_id)
+{
+ struct interface *ifp;
+
+ ifp = zebra_interface_state_read(client->ibuf, vrf_id);
+ if (ifp == NULL)
+ return 0;
+
+ debugf(NHRP_DEBUG_IF, "if-up: %s", ifp->name);
+ nhrp_interface_update_nbma(ifp);
+
+ return 0;
+}
+
+int nhrp_interface_down(int cmd, struct zclient *client,
+ zebra_size_t length, vrf_id_t vrf_id)
+{
+ struct interface *ifp;
+
+ ifp = zebra_interface_state_read(client->ibuf, vrf_id);
+ if (ifp == NULL)
+ return 0;
+
+ debugf(NHRP_DEBUG_IF, "if-down: %s", ifp->name);
+ nhrp_interface_update(ifp);
+ return 0;
+}
+
+int nhrp_interface_address_add(int cmd, struct zclient *client,
+ zebra_size_t length, vrf_id_t vrf_id)
+{
+ struct connected *ifc;
+ char buf[PREFIX_STRLEN];
+
+ ifc = zebra_interface_address_read(cmd, client->ibuf, vrf_id);
+ if (ifc == NULL)
+ return 0;
+
+ debugf(NHRP_DEBUG_IF, "if-addr-add: %s: %s",
+ ifc->ifp->name,
+ prefix2str(ifc->address, buf, sizeof buf));
+
+ nhrp_interface_update_address(ifc->ifp, family2afi(PREFIX_FAMILY(ifc->address)), 0);
+
+ return 0;
+}
+
+int nhrp_interface_address_delete(int cmd, struct zclient *client,
+ zebra_size_t length, vrf_id_t vrf_id)
+{
+ struct connected *ifc;
+ char buf[PREFIX_STRLEN];
+
+ ifc = zebra_interface_address_read(cmd, client->ibuf, vrf_id);
+ if (ifc == NULL)
+ return 0;
+
+ debugf(NHRP_DEBUG_IF, "if-addr-del: %s: %s",
+ ifc->ifp->name,
+ prefix2str(ifc->address, buf, sizeof buf));
+
+ nhrp_interface_update_address(ifc->ifp, family2afi(PREFIX_FAMILY(ifc->address)), 0);
+ connected_free(ifc);
+
+ return 0;
+}
+
+void nhrp_interface_notify_add(struct interface *ifp, struct notifier_block *n, notifier_fn_t fn)
+{
+ struct nhrp_interface *nifp = ifp->info;
+ notifier_add(n, &nifp->notifier_list, fn);
+}
+
+void nhrp_interface_notify_del(struct interface *ifp, struct notifier_block *n)
+{
+ notifier_del(n);
+}
+
+void nhrp_interface_set_protection(struct interface *ifp, const char *profile, const char *fallback_profile)
+{
+ struct nhrp_interface *nifp = ifp->info;
+
+ if (nifp->ipsec_profile) free(nifp->ipsec_profile);
+ nifp->ipsec_profile = profile ? strdup(profile) : NULL;
+
+ if (nifp->ipsec_fallback_profile) free(nifp->ipsec_fallback_profile);
+ nifp->ipsec_fallback_profile = fallback_profile ? strdup(fallback_profile) : NULL;
+}
+
+void nhrp_interface_set_source(struct interface *ifp, const char *ifname)
+{
+ struct nhrp_interface *nifp = ifp->info;
+
+ if (nifp->source) free(nifp->source);
+ nifp->source = ifname ? strdup(ifname) : NULL;
+
+ nhrp_interface_update_nbma(ifp);
+}
diff --git a/nhrpd/nhrp_main.c b/nhrpd/nhrp_main.c
new file mode 100644
index 0000000000..2f6ee1c04f
--- /dev/null
+++ b/nhrpd/nhrp_main.c
@@ -0,0 +1,154 @@
+/* NHRP daemon main functions
+ * Copyright (c) 2014-2015 Timo Teräs
+ *
+ * This file is free software: you may copy, redistribute 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.
+ */
+
+#include <unistd.h>
+
+#include "zebra.h"
+#include "privs.h"
+#include "getopt.h"
+#include "thread.h"
+#include "sigevent.h"
+#include "version.h"
+#include "log.h"
+#include "memory.h"
+#include "memory_vty.h"
+#include "command.h"
+#include "libfrr.h"
+
+#include "nhrpd.h"
+#include "netlink.h"
+
+DEFINE_MGROUP(NHRPD, "NHRP")
+
+unsigned int debug_flags = 0;
+
+struct thread_master *master;
+struct timeval current_time;
+
+/* nhrpd options. */
+struct option longopts[] = {
+ { 0 }
+};
+
+/* nhrpd privileges */
+static zebra_capabilities_t _caps_p [] = {
+ ZCAP_NET_RAW,
+ ZCAP_NET_ADMIN,
+ ZCAP_DAC_OVERRIDE, /* for now needed to write to /proc/sys/net/ipv4/<if>/send_redirect */
+};
+
+static struct zebra_privs_t nhrpd_privs = {
+#ifdef QUAGGA_USER
+ .user = QUAGGA_USER,
+#endif
+#ifdef QUAGGA_GROUP
+ .group = QUAGGA_GROUP,
+#endif
+#ifdef VTY_GROUP
+ .vty_group = VTY_GROUP,
+#endif
+ .caps_p = _caps_p,
+ .cap_num_p = ZEBRA_NUM_OF(_caps_p),
+};
+
+static void parse_arguments(int argc, char **argv)
+{
+ int opt;
+
+ while (1) {
+ opt = frr_getopt(argc, argv, 0);
+ if(opt < 0) break;
+
+ switch (opt) {
+ case 0:
+ break;
+ default:
+ frr_help_exit(1);
+ break;
+ }
+ }
+}
+
+static void nhrp_sigusr1(void)
+{
+ zlog_rotate();
+}
+
+static void nhrp_request_stop(void)
+{
+ debugf(NHRP_DEBUG_COMMON, "Exiting...");
+
+ nhrp_shortcut_terminate();
+ nhrp_nhs_terminate();
+ nhrp_zebra_terminate();
+ vici_terminate();
+ evmgr_terminate();
+ nhrp_vc_terminate();
+ vrf_terminate();
+ /* memory_terminate(); */
+ /* vty_terminate(); */
+ cmd_terminate();
+ /* signal_terminate(); */
+ zprivs_terminate(&nhrpd_privs);
+
+ debugf(NHRP_DEBUG_COMMON, "Done.");
+
+ closezlog();
+
+ exit(0);
+}
+
+static struct quagga_signal_t sighandlers[] = {
+ { .signal = SIGUSR1, .handler = &nhrp_sigusr1, },
+ { .signal = SIGINT, .handler = &nhrp_request_stop, },
+ { .signal = SIGTERM, .handler = &nhrp_request_stop, },
+};
+
+FRR_DAEMON_INFO(nhrpd, NHRP,
+ .vty_port = NHRP_VTY_PORT,
+
+ .proghelp = "Implementation of the NHRP routing protocol.",
+
+ .signals = sighandlers,
+ .n_signals = array_size(sighandlers),
+
+ .privs = &nhrpd_privs,
+)
+
+int main(int argc, char **argv)
+{
+ frr_preinit(&nhrpd_di, argc, argv);
+ frr_opt_add("", longopts, "");
+
+ parse_arguments(argc, argv);
+
+ /* Library inits. */
+ master = frr_init();
+ nhrp_interface_init();
+ vrf_init();
+ resolver_init();
+
+ /* Run with elevated capabilities, as for all netlink activity
+ * we need privileges anyway. */
+ nhrpd_privs.change(ZPRIVS_RAISE);
+
+ netlink_init();
+ evmgr_init();
+ nhrp_vc_init();
+ nhrp_packet_init();
+ vici_init();
+ nhrp_zebra_init();
+ nhrp_shortcut_init();
+
+ nhrp_config_init();
+
+ frr_config_fork();
+ frr_run(master);
+ return 0;
+}
diff --git a/nhrpd/nhrp_nhs.c b/nhrpd/nhrp_nhs.c
new file mode 100644
index 0000000000..10245d308a
--- /dev/null
+++ b/nhrpd/nhrp_nhs.c
@@ -0,0 +1,372 @@
+/* NHRP NHC nexthop server functions (registration)
+ * Copyright (c) 2014-2015 Timo Teräs
+ *
+ * This file is free software: you may copy, redistribute 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.
+ */
+
+#include "zebra.h"
+#include "zbuf.h"
+#include "memory.h"
+#include "thread.h"
+#include "nhrpd.h"
+#include "nhrp_protocol.h"
+
+DEFINE_MTYPE_STATIC(NHRPD, NHRP_NHS, "NHRP next hop server")
+DEFINE_MTYPE_STATIC(NHRPD, NHRP_REGISTRATION, "NHRP registration entries")
+
+static int nhrp_nhs_resolve(struct thread *t);
+
+struct nhrp_registration {
+ struct list_head reglist_entry;
+ struct thread *t_register;
+ struct nhrp_nhs *nhs;
+ struct nhrp_reqid reqid;
+ unsigned int timeout;
+ unsigned mark : 1;
+ union sockunion proto_addr;
+ struct nhrp_peer *peer;
+ struct notifier_block peer_notifier;
+};
+
+static int nhrp_reg_send_req(struct thread *t);
+
+static void nhrp_reg_reply(struct nhrp_reqid *reqid, void *arg)
+{
+ struct nhrp_packet_parser *p = arg;
+ struct nhrp_registration *r = container_of(reqid, struct nhrp_registration, reqid);
+ struct nhrp_nhs *nhs = r->nhs;
+ struct interface *ifp = nhs->ifp;
+ struct nhrp_interface *nifp = ifp->info;
+ struct nhrp_extension_header *ext;
+ struct nhrp_cie_header *cie;
+ struct nhrp_cache *c;
+ struct zbuf extpl;
+ union sockunion cie_nbma, cie_proto, *proto;
+ char buf[64];
+ int ok = 0, holdtime;
+
+ nhrp_reqid_free(&nhrp_packet_reqid, &r->reqid);
+
+ if (p->hdr->type != NHRP_PACKET_REGISTRATION_REPLY) {
+ debugf(NHRP_DEBUG_COMMON, "NHS: Registration failed");
+ return;
+ }
+
+ debugf(NHRP_DEBUG_COMMON, "NHS: Reg.reply received");
+
+ ok = 1;
+ while ((cie = nhrp_cie_pull(&p->payload, p->hdr, &cie_nbma, &cie_proto)) != NULL) {
+ proto = sockunion_family(&cie_proto) != AF_UNSPEC ? &cie_proto : &p->src_proto;
+ debugf(NHRP_DEBUG_COMMON, "NHS: CIE registration: %s: %d",
+ sockunion2str(proto, buf, sizeof(buf)),
+ cie->code);
+ if (!((cie->code == NHRP_CODE_SUCCESS) ||
+ (cie->code == NHRP_CODE_ADMINISTRATIVELY_PROHIBITED && nhs->hub)))
+ ok = 0;
+ }
+
+ if (!ok)
+ return;
+
+ /* Parse extensions */
+ sockunion_family(&nifp->nat_nbma) = AF_UNSPEC;
+ while ((ext = nhrp_ext_pull(&p->extensions, &extpl)) != NULL) {
+ switch (htons(ext->type) & ~NHRP_EXTENSION_FLAG_COMPULSORY) {
+ case NHRP_EXTENSION_NAT_ADDRESS:
+ /* NHS adds second CIE if NAT is detected */
+ if (nhrp_cie_pull(&extpl, p->hdr, &cie_nbma, &cie_proto) &&
+ nhrp_cie_pull(&extpl, p->hdr, &cie_nbma, &cie_proto)) {
+ nifp->nat_nbma = cie_nbma;
+ debugf(NHRP_DEBUG_IF, "%s: NAT detected, real NBMA address: %s",
+ ifp->name, sockunion2str(&nifp->nbma, buf, sizeof(buf)));
+ }
+ break;
+ }
+ }
+
+ /* Success - schedule next registration, and route NHS */
+ r->timeout = 2;
+ holdtime = nifp->afi[nhs->afi].holdtime;
+ THREAD_OFF(r->t_register);
+
+ /* RFC 2332 5.2.3 - Registration is recommend to be renewed
+ * every one third of holdtime */
+ THREAD_TIMER_ON(master, r->t_register, nhrp_reg_send_req, r, holdtime / 3);
+
+ r->proto_addr = p->dst_proto;
+ c = nhrp_cache_get(ifp, &p->dst_proto, 1);
+ if (c) nhrp_cache_update_binding(c, NHRP_CACHE_NHS, holdtime, nhrp_peer_ref(r->peer), 0, NULL);
+}
+
+static int nhrp_reg_timeout(struct thread *t)
+{
+ struct nhrp_registration *r = THREAD_ARG(t);
+ struct nhrp_cache *c;
+
+ r->t_register = NULL;
+
+ if (r->timeout >= 16 && sockunion_family(&r->proto_addr) != AF_UNSPEC) {
+ nhrp_reqid_free(&nhrp_packet_reqid, &r->reqid);
+ c = nhrp_cache_get(r->nhs->ifp, &r->proto_addr, 0);
+ if (c) nhrp_cache_update_binding(c, NHRP_CACHE_NHS, -1, NULL, 0, NULL);
+ sockunion_family(&r->proto_addr) = AF_UNSPEC;
+ }
+
+ r->timeout <<= 1;
+ if (r->timeout > 64) r->timeout = 2;
+ THREAD_TIMER_MSEC_ON(master, r->t_register, nhrp_reg_send_req, r, 10);
+
+ return 0;
+}
+
+static void nhrp_reg_peer_notify(struct notifier_block *n, unsigned long cmd)
+{
+ struct nhrp_registration *r = container_of(n, struct nhrp_registration, peer_notifier);
+ char buf[SU_ADDRSTRLEN];
+
+ switch (cmd) {
+ case NOTIFY_PEER_UP:
+ case NOTIFY_PEER_DOWN:
+ case NOTIFY_PEER_IFCONFIG_CHANGED:
+ case NOTIFY_PEER_MTU_CHANGED:
+ debugf(NHRP_DEBUG_COMMON, "NHS: Flush timer for %s",
+ sockunion2str(&r->peer->vc->remote.nbma, buf, sizeof buf));
+ THREAD_TIMER_OFF(r->t_register);
+ THREAD_TIMER_MSEC_ON(master, r->t_register, nhrp_reg_send_req, r, 10);
+ break;
+ }
+}
+
+static int nhrp_reg_send_req(struct thread *t)
+{
+ struct nhrp_registration *r = THREAD_ARG(t);
+ struct nhrp_nhs *nhs = r->nhs;
+ char buf1[SU_ADDRSTRLEN], buf2[SU_ADDRSTRLEN];
+ struct interface *ifp = nhs->ifp;
+ struct nhrp_interface *nifp = ifp->info;
+ struct nhrp_afi_data *if_ad = &nifp->afi[nhs->afi];
+ union sockunion *dst_proto;
+ struct zbuf *zb;
+ struct nhrp_packet_header *hdr;
+ struct nhrp_extension_header *ext;
+ struct nhrp_cie_header *cie;
+
+ r->t_register = NULL;
+ if (!nhrp_peer_check(r->peer, 2)) {
+ debugf(NHRP_DEBUG_COMMON, "NHS: Waiting link for %s",
+ sockunion2str(&r->peer->vc->remote.nbma, buf1, sizeof buf1));
+ THREAD_TIMER_ON(master, r->t_register, nhrp_reg_send_req, r, 120);
+ return 0;
+ }
+
+ THREAD_TIMER_ON(master, r->t_register, nhrp_reg_timeout, r, r->timeout);
+
+ /* RFC2332 5.2.3 NHC uses it's own address as dst if NHS is unknown */
+ dst_proto = &nhs->proto_addr;
+ if (sockunion_family(dst_proto) == AF_UNSPEC)
+ dst_proto = &if_ad->addr;
+
+ sockunion2str(&if_ad->addr, buf1, sizeof(buf1));
+ sockunion2str(dst_proto, buf2, sizeof(buf2));
+ debugf(NHRP_DEBUG_COMMON, "NHS: Register %s -> %s (timeout %d)", buf1, buf2, r->timeout);
+
+ /* No protocol address configured for tunnel interface */
+ if (sockunion_family(&if_ad->addr) == AF_UNSPEC)
+ return 0;
+
+ zb = zbuf_alloc(1400);
+ hdr = nhrp_packet_push(zb, NHRP_PACKET_REGISTRATION_REQUEST, &nifp->nbma, &if_ad->addr, dst_proto);
+ hdr->hop_count = 0;
+ if (!(if_ad->flags & NHRP_IFF_REG_NO_UNIQUE))
+ hdr->flags |= htons(NHRP_FLAG_REGISTRATION_UNIQUE);
+
+ hdr->u.request_id = htonl(nhrp_reqid_alloc(&nhrp_packet_reqid, &r->reqid, nhrp_reg_reply));
+
+ /* FIXME: push CIE for each local protocol address */
+ cie = nhrp_cie_push(zb, NHRP_CODE_SUCCESS, NULL, NULL);
+ cie->prefix_length = 0xff;
+ cie->holding_time = htons(if_ad->holdtime);
+ cie->mtu = htons(if_ad->mtu);
+
+ nhrp_ext_request(zb, hdr, ifp);
+
+ /* Cisco NAT detection extension */
+ hdr->flags |= htons(NHRP_FLAG_REGISTRATION_NAT);
+ ext = nhrp_ext_push(zb, hdr, NHRP_EXTENSION_NAT_ADDRESS);
+ cie = nhrp_cie_push(zb, NHRP_CODE_SUCCESS, &nifp->nbma, &if_ad->addr);
+ cie->prefix_length = 8 * sockunion_get_addrlen(&nifp->nbma);
+ nhrp_ext_complete(zb, ext);
+
+ nhrp_packet_complete(zb, hdr);
+ nhrp_peer_send(r->peer, zb);
+ zbuf_free(zb);
+
+ return 0;
+}
+
+static void nhrp_reg_delete(struct nhrp_registration *r)
+{
+ nhrp_peer_notify_del(r->peer, &r->peer_notifier);
+ nhrp_peer_unref(r->peer);
+ list_del(&r->reglist_entry);
+ THREAD_OFF(r->t_register);
+ XFREE(MTYPE_NHRP_REGISTRATION, r);
+}
+
+static struct nhrp_registration *nhrp_reg_by_nbma(struct nhrp_nhs *nhs, const union sockunion *nbma_addr)
+{
+ struct nhrp_registration *r;
+
+ list_for_each_entry(r, &nhs->reglist_head, reglist_entry)
+ if (sockunion_same(&r->peer->vc->remote.nbma, nbma_addr))
+ return r;
+ return NULL;
+}
+
+static void nhrp_nhs_resolve_cb(struct resolver_query *q, int n, union sockunion *addrs)
+{
+ struct nhrp_nhs *nhs = container_of(q, struct nhrp_nhs, dns_resolve);
+ struct nhrp_interface *nifp = nhs->ifp->info;
+ struct nhrp_registration *reg, *regn;
+ int i;
+
+ nhs->t_resolve = NULL;
+ if (n < 0) {
+ /* Failed, retry in a moment */
+ THREAD_TIMER_ON(master, nhs->t_resolve, nhrp_nhs_resolve, nhs, 5);
+ return;
+ }
+
+ THREAD_TIMER_ON(master, nhs->t_resolve, nhrp_nhs_resolve, nhs, 2*60*60);
+
+ list_for_each_entry(reg, &nhs->reglist_head, reglist_entry)
+ reg->mark = 1;
+
+ nhs->hub = 0;
+ for (i = 0; i < n; i++) {
+ if (sockunion_same(&addrs[i], &nifp->nbma)) {
+ nhs->hub = 1;
+ continue;
+ }
+
+ reg = nhrp_reg_by_nbma(nhs, &addrs[i]);
+ if (reg) {
+ reg->mark = 0;
+ continue;
+ }
+
+ reg = XCALLOC(MTYPE_NHRP_REGISTRATION, sizeof(*reg));
+ reg->peer = nhrp_peer_get(nhs->ifp, &addrs[i]);
+ reg->nhs = nhs;
+ reg->timeout = 1;
+ list_init(&reg->reglist_entry);
+ list_add_tail(&reg->reglist_entry, &nhs->reglist_head);
+ nhrp_peer_notify_add(reg->peer, &reg->peer_notifier, nhrp_reg_peer_notify);
+ THREAD_TIMER_MSEC_ON(master, reg->t_register, nhrp_reg_send_req, reg, 50);
+ }
+
+ list_for_each_entry_safe(reg, regn, &nhs->reglist_head, reglist_entry) {
+ if (reg->mark)
+ nhrp_reg_delete(reg);
+ }
+}
+
+static int nhrp_nhs_resolve(struct thread *t)
+{
+ struct nhrp_nhs *nhs = THREAD_ARG(t);
+
+ resolver_resolve(&nhs->dns_resolve, AF_INET, nhs->nbma_fqdn, nhrp_nhs_resolve_cb);
+
+ return 0;
+}
+
+int nhrp_nhs_add(struct interface *ifp, afi_t afi, union sockunion *proto_addr, const char *nbma_fqdn)
+{
+ struct nhrp_interface *nifp = ifp->info;
+ struct nhrp_nhs *nhs;
+
+ if (sockunion_family(proto_addr) != AF_UNSPEC &&
+ sockunion_family(proto_addr) != afi2family(afi))
+ return NHRP_ERR_PROTOCOL_ADDRESS_MISMATCH;
+
+ list_for_each_entry(nhs, &nifp->afi[afi].nhslist_head, nhslist_entry) {
+ if (sockunion_family(&nhs->proto_addr) != AF_UNSPEC &&
+ sockunion_family(proto_addr) != AF_UNSPEC &&
+ sockunion_same(&nhs->proto_addr, proto_addr))
+ return NHRP_ERR_ENTRY_EXISTS;
+
+ if (strcmp(nhs->nbma_fqdn, nbma_fqdn) == 0)
+ return NHRP_ERR_ENTRY_EXISTS;
+ }
+
+ nhs = XMALLOC(MTYPE_NHRP_NHS, sizeof(struct nhrp_nhs));
+ if (!nhs) return NHRP_ERR_NO_MEMORY;
+
+ *nhs = (struct nhrp_nhs) {
+ .afi = afi,
+ .ifp = ifp,
+ .proto_addr = *proto_addr,
+ .nbma_fqdn = strdup(nbma_fqdn),
+ .reglist_head = LIST_INITIALIZER(nhs->reglist_head),
+ };
+ list_add_tail(&nhs->nhslist_entry, &nifp->afi[afi].nhslist_head);
+ THREAD_TIMER_MSEC_ON(master, nhs->t_resolve, nhrp_nhs_resolve, nhs, 1000);
+
+ return NHRP_OK;
+}
+
+int nhrp_nhs_del(struct interface *ifp, afi_t afi, union sockunion *proto_addr, const char *nbma_fqdn)
+{
+ struct nhrp_interface *nifp = ifp->info;
+ struct nhrp_nhs *nhs, *nnhs;
+ int ret = NHRP_ERR_ENTRY_NOT_FOUND;
+
+ if (sockunion_family(proto_addr) != AF_UNSPEC &&
+ sockunion_family(proto_addr) != afi2family(afi))
+ return NHRP_ERR_PROTOCOL_ADDRESS_MISMATCH;
+
+ list_for_each_entry_safe(nhs, nnhs, &nifp->afi[afi].nhslist_head, nhslist_entry) {
+ if (!sockunion_same(&nhs->proto_addr, proto_addr))
+ continue;
+ if (strcmp(nhs->nbma_fqdn, nbma_fqdn) != 0)
+ continue;
+
+ nhrp_nhs_free(nhs);
+ ret = NHRP_OK;
+ }
+
+ return ret;
+}
+
+int nhrp_nhs_free(struct nhrp_nhs *nhs)
+{
+ struct nhrp_registration *r, *rn;
+
+ list_for_each_entry_safe(r, rn, &nhs->reglist_head, reglist_entry)
+ nhrp_reg_delete(r);
+ THREAD_OFF(nhs->t_resolve);
+ list_del(&nhs->nhslist_entry);
+ free((void*) nhs->nbma_fqdn);
+ XFREE(MTYPE_NHRP_NHS, nhs);
+ return 0;
+}
+
+void nhrp_nhs_terminate(void)
+{
+ struct interface *ifp;
+ struct nhrp_interface *nifp;
+ struct nhrp_nhs *nhs, *tmp;
+ struct listnode *node;
+ afi_t afi;
+
+ for (ALL_LIST_ELEMENTS_RO(vrf_iflist (VRF_DEFAULT), node, ifp)) {
+ nifp = ifp->info;
+ for (afi = 0; afi < AFI_MAX; afi++) {
+ list_for_each_entry_safe(nhs, tmp, &nifp->afi[afi].nhslist_head, nhslist_entry)
+ nhrp_nhs_free(nhs);
+ }
+ }
+}
diff --git a/nhrpd/nhrp_packet.c b/nhrpd/nhrp_packet.c
new file mode 100644
index 0000000000..5d2866a67e
--- /dev/null
+++ b/nhrpd/nhrp_packet.c
@@ -0,0 +1,312 @@
+/* NHRP packet handling functions
+ * Copyright (c) 2014-2015 Timo Teräs
+ *
+ * This file is free software: you may copy, redistribute 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.
+ */
+
+#include <netinet/if_ether.h>
+#include "nhrpd.h"
+#include "zbuf.h"
+#include "thread.h"
+#include "hash.h"
+
+#include "nhrp_protocol.h"
+#include "os.h"
+
+struct nhrp_reqid_pool nhrp_packet_reqid;
+
+static uint16_t family2proto(int family)
+{
+ switch (family) {
+ case AF_INET: return ETH_P_IP;
+ case AF_INET6: return ETH_P_IPV6;
+ }
+ return 0;
+}
+
+static int proto2family(uint16_t proto)
+{
+ switch (proto) {
+ case ETH_P_IP: return AF_INET;
+ case ETH_P_IPV6: return AF_INET6;
+ }
+ return AF_UNSPEC;
+}
+
+struct nhrp_packet_header *nhrp_packet_push(
+ struct zbuf *zb, uint8_t type,
+ const union sockunion *src_nbma,
+ const union sockunion *src_proto,
+ const union sockunion *dst_proto)
+{
+ struct nhrp_packet_header *hdr;
+
+ hdr = zbuf_push(zb, struct nhrp_packet_header);
+ if (!hdr) return NULL;
+
+ *hdr = (struct nhrp_packet_header) {
+ .afnum = htons(family2afi(sockunion_family(src_nbma))),
+ .protocol_type = htons(family2proto(sockunion_family(src_proto))),
+ .version = NHRP_VERSION_RFC2332,
+ .type = type,
+ .hop_count = 64,
+ .src_nbma_address_len = sockunion_get_addrlen(src_nbma),
+ .src_protocol_address_len = sockunion_get_addrlen(src_proto),
+ .dst_protocol_address_len = sockunion_get_addrlen(dst_proto),
+ };
+
+ zbuf_put(zb, sockunion_get_addr(src_nbma), hdr->src_nbma_address_len);
+ zbuf_put(zb, sockunion_get_addr(src_proto), hdr->src_protocol_address_len);
+ zbuf_put(zb, sockunion_get_addr(dst_proto), hdr->dst_protocol_address_len);
+
+ return hdr;
+}
+
+struct nhrp_packet_header *nhrp_packet_pull(
+ struct zbuf *zb,
+ union sockunion *src_nbma,
+ union sockunion *src_proto,
+ union sockunion *dst_proto)
+{
+ struct nhrp_packet_header *hdr;
+
+ hdr = zbuf_pull(zb, struct nhrp_packet_header);
+ if (!hdr) return NULL;
+
+ sockunion_set(
+ src_nbma, afi2family(htons(hdr->afnum)),
+ zbuf_pulln(zb, hdr->src_nbma_address_len + hdr->src_nbma_subaddress_len),
+ hdr->src_nbma_address_len + hdr->src_nbma_subaddress_len);
+ sockunion_set(
+ src_proto, proto2family(htons(hdr->protocol_type)),
+ zbuf_pulln(zb, hdr->src_protocol_address_len),
+ hdr->src_protocol_address_len);
+ sockunion_set(
+ dst_proto, proto2family(htons(hdr->protocol_type)),
+ zbuf_pulln(zb, hdr->dst_protocol_address_len),
+ hdr->dst_protocol_address_len);
+
+ return hdr;
+}
+
+uint16_t nhrp_packet_calculate_checksum(const uint8_t *pdu, uint16_t len)
+{
+ const uint16_t *pdu16 = (const uint16_t *) pdu;
+ uint32_t csum = 0;
+ int i;
+
+ for (i = 0; i < len / 2; i++)
+ csum += pdu16[i];
+ if (len & 1)
+ csum += htons(pdu[len - 1]);
+
+ while (csum & 0xffff0000)
+ csum = (csum & 0xffff) + (csum >> 16);
+
+ return (~csum) & 0xffff;
+}
+
+void nhrp_packet_complete(struct zbuf *zb, struct nhrp_packet_header *hdr)
+{
+ unsigned short size;
+
+ if (hdr->extension_offset)
+ nhrp_ext_push(zb, hdr, NHRP_EXTENSION_END | NHRP_EXTENSION_FLAG_COMPULSORY);
+
+ size = zb->tail - (uint8_t *)hdr;
+ hdr->packet_size = htons(size);
+ hdr->checksum = 0;
+ hdr->checksum = nhrp_packet_calculate_checksum((uint8_t *) hdr, size);
+}
+
+struct nhrp_cie_header *nhrp_cie_push(
+ struct zbuf *zb,
+ uint8_t code,
+ const union sockunion *nbma,
+ const union sockunion *proto)
+{
+ struct nhrp_cie_header *cie;
+
+ cie = zbuf_push(zb, struct nhrp_cie_header);
+ *cie = (struct nhrp_cie_header) {
+ .code = code,
+ };
+ if (nbma) {
+ cie->nbma_address_len = sockunion_get_addrlen(nbma);
+ zbuf_put(zb, sockunion_get_addr(nbma), cie->nbma_address_len);
+ }
+ if (proto) {
+ cie->protocol_address_len = sockunion_get_addrlen(proto);
+ zbuf_put(zb, sockunion_get_addr(proto), cie->protocol_address_len);
+ }
+
+ return cie;
+}
+
+struct nhrp_cie_header *nhrp_cie_pull(
+ struct zbuf *zb,
+ struct nhrp_packet_header *hdr,
+ union sockunion *nbma,
+ union sockunion *proto)
+{
+ struct nhrp_cie_header *cie;
+
+ cie = zbuf_pull(zb, struct nhrp_cie_header);
+ if (!cie) return NULL;
+
+ if (cie->nbma_address_len + cie->nbma_subaddress_len) {
+ sockunion_set(
+ nbma, afi2family(htons(hdr->afnum)),
+ zbuf_pulln(zb, cie->nbma_address_len + cie->nbma_subaddress_len),
+ cie->nbma_address_len + cie->nbma_subaddress_len);
+ } else {
+ sockunion_family(nbma) = AF_UNSPEC;
+ }
+
+ if (cie->protocol_address_len) {
+ sockunion_set(
+ proto, proto2family(htons(hdr->protocol_type)),
+ zbuf_pulln(zb, cie->protocol_address_len),
+ cie->protocol_address_len);
+ } else {
+ sockunion_family(proto) = AF_UNSPEC;
+ }
+
+ return cie;
+}
+
+struct nhrp_extension_header *nhrp_ext_push(struct zbuf *zb, struct nhrp_packet_header *hdr, uint16_t type)
+{
+ struct nhrp_extension_header *ext;
+ ext = zbuf_push(zb, struct nhrp_extension_header);
+ if (!ext) return NULL;
+
+ if (!hdr->extension_offset)
+ hdr->extension_offset = htons(zb->tail - (uint8_t*) hdr - sizeof(struct nhrp_extension_header));
+
+ *ext = (struct nhrp_extension_header) {
+ .type = htons(type),
+ .length = 0,
+ };
+ return ext;
+}
+
+void nhrp_ext_complete(struct zbuf *zb, struct nhrp_extension_header *ext)
+{
+ ext->length = htons(zb->tail - (uint8_t*)ext - sizeof(struct nhrp_extension_header));
+}
+
+struct nhrp_extension_header *nhrp_ext_pull(struct zbuf *zb, struct zbuf *payload)
+{
+ struct nhrp_extension_header *ext;
+ uint16_t plen;
+
+ ext = zbuf_pull(zb, struct nhrp_extension_header);
+ if (!ext) return NULL;
+
+ plen = htons(ext->length);
+ zbuf_init(payload, zbuf_pulln(zb, plen), plen, plen);
+ return ext;
+}
+
+void nhrp_ext_request(struct zbuf *zb, struct nhrp_packet_header *hdr, struct interface *ifp)
+{
+ /* Place holders for standard extensions */
+ nhrp_ext_push(zb, hdr, NHRP_EXTENSION_FORWARD_TRANSIT_NHS | NHRP_EXTENSION_FLAG_COMPULSORY);
+ nhrp_ext_push(zb, hdr, NHRP_EXTENSION_REVERSE_TRANSIT_NHS | NHRP_EXTENSION_FLAG_COMPULSORY);
+ nhrp_ext_push(zb, hdr, NHRP_EXTENSION_RESPONDER_ADDRESS | NHRP_EXTENSION_FLAG_COMPULSORY);
+}
+
+int nhrp_ext_reply(struct zbuf *zb, struct nhrp_packet_header *hdr, struct interface *ifp, struct nhrp_extension_header *ext, struct zbuf *extpayload)
+{
+ struct nhrp_interface *nifp = ifp->info;
+ struct nhrp_afi_data *ad = &nifp->afi[htons(hdr->afnum)];
+ struct nhrp_extension_header *dst;
+ struct nhrp_cie_header *cie;
+ uint16_t type;
+
+ type = htons(ext->type) & ~NHRP_EXTENSION_FLAG_COMPULSORY;
+ if (type == NHRP_EXTENSION_END)
+ return 0;
+
+ dst = nhrp_ext_push(zb, hdr, htons(ext->type));
+ if (!dst) goto err;
+
+ switch (type) {
+ case NHRP_EXTENSION_RESPONDER_ADDRESS:
+ cie = nhrp_cie_push(zb, NHRP_CODE_SUCCESS, &nifp->nbma, &ad->addr);
+ if (!cie) goto err;
+ cie->holding_time = htons(ad->holdtime);
+ break;
+ default:
+ if (type & NHRP_EXTENSION_FLAG_COMPULSORY)
+ goto err;
+ case NHRP_EXTENSION_FORWARD_TRANSIT_NHS:
+ case NHRP_EXTENSION_REVERSE_TRANSIT_NHS:
+ /* Supported compulsory extensions, and any
+ * non-compulsory that is not explicitly handled,
+ * should be just copied. */
+ zbuf_copy(zb, extpayload, zbuf_used(extpayload));
+ break;
+ }
+ nhrp_ext_complete(zb, dst);
+ return 0;
+err:
+ zbuf_set_werror(zb);
+ return -1;
+}
+
+static int nhrp_packet_recvraw(struct thread *t)
+{
+ int fd = THREAD_FD(t), ifindex;
+ struct zbuf *zb;
+ struct interface *ifp;
+ struct nhrp_peer *p;
+ union sockunion remote_nbma;
+ uint8_t addr[64];
+ size_t len, addrlen;
+
+ thread_add_read(master, nhrp_packet_recvraw, 0, fd);
+
+ zb = zbuf_alloc(1500);
+ if (!zb) return 0;
+
+ len = zbuf_size(zb);
+ addrlen = sizeof(addr);
+ if (os_recvmsg(zb->buf, &len, &ifindex, addr, &addrlen) < 0)
+ goto err;
+
+ zb->head = zb->buf;
+ zb->tail = zb->buf + len;
+
+ switch (addrlen) {
+ case 4:
+ sockunion_set(&remote_nbma, AF_INET, addr, addrlen);
+ break;
+ default:
+ goto err;
+ }
+
+ ifp = if_lookup_by_index(ifindex);
+ if (!ifp) goto err;
+
+ p = nhrp_peer_get(ifp, &remote_nbma);
+ if (!p) goto err;
+
+ nhrp_peer_recv(p, zb);
+ nhrp_peer_unref(p);
+ return 0;
+
+err:
+ zbuf_free(zb);
+ return 0;
+}
+
+int nhrp_packet_init(void)
+{
+ thread_add_read(master, nhrp_packet_recvraw, 0, os_socket());
+ return 0;
+}
diff --git a/nhrpd/nhrp_peer.c b/nhrpd/nhrp_peer.c
new file mode 100644
index 0000000000..73b6aaccf6
--- /dev/null
+++ b/nhrpd/nhrp_peer.c
@@ -0,0 +1,862 @@
+/* NHRP peer functions
+ * Copyright (c) 2014-2015 Timo Teräs
+ *
+ * This file is free software: you may copy, redistribute 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.
+ */
+
+#include <netinet/if_ether.h>
+
+#include "zebra.h"
+#include "memory.h"
+#include "thread.h"
+#include "hash.h"
+
+#include "nhrpd.h"
+#include "nhrp_protocol.h"
+#include "os.h"
+
+DEFINE_MTYPE_STATIC(NHRPD, NHRP_PEER, "NHRP peer entry")
+
+struct ipv6hdr {
+ uint8_t priority_version;
+ uint8_t flow_lbl[3];
+ uint16_t payload_len;
+ uint8_t nexthdr;
+ uint8_t hop_limit;
+ struct in6_addr saddr;
+ struct in6_addr daddr;
+};
+
+static void nhrp_packet_debug(struct zbuf *zb, const char *dir);
+
+static void nhrp_peer_check_delete(struct nhrp_peer *p)
+{
+ struct nhrp_interface *nifp = p->ifp->info;
+
+ if (p->ref || notifier_active(&p->notifier_list))
+ return;
+
+ THREAD_OFF(p->t_fallback);
+ hash_release(nifp->peer_hash, p);
+ nhrp_interface_notify_del(p->ifp, &p->ifp_notifier);
+ nhrp_vc_notify_del(p->vc, &p->vc_notifier);
+ XFREE(MTYPE_NHRP_PEER, p);
+}
+
+static int nhrp_peer_notify_up(struct thread *t)
+{
+ struct nhrp_peer *p = THREAD_ARG(t);
+ struct nhrp_vc *vc = p->vc;
+ struct interface *ifp = p->ifp;
+ struct nhrp_interface *nifp = ifp->info;
+
+ p->t_fallback = NULL;
+ if (nifp->enabled && (!nifp->ipsec_profile || vc->ipsec)) {
+ p->online = 1;
+ nhrp_peer_ref(p);
+ notifier_call(&p->notifier_list, NOTIFY_PEER_UP);
+ nhrp_peer_unref(p);
+ }
+
+ return 0;
+}
+
+static void __nhrp_peer_check(struct nhrp_peer *p)
+{
+ struct nhrp_vc *vc = p->vc;
+ struct interface *ifp = p->ifp;
+ struct nhrp_interface *nifp = ifp->info;
+ unsigned online;
+
+ online = nifp->enabled && (!nifp->ipsec_profile || vc->ipsec);
+ if (p->online != online) {
+ THREAD_OFF(p->t_fallback);
+ if (online && notifier_active(&p->notifier_list)) {
+ /* If we requested the IPsec connection, delay
+ * the up notification a bit to allow things
+ * settle down. This allows IKE to install
+ * SPDs and SAs. */
+ THREAD_TIMER_MSEC_ON(
+ master, p->t_fallback,
+ nhrp_peer_notify_up, p, 50);
+ } else {
+ nhrp_peer_ref(p);
+ p->online = online;
+ if (online) {
+ notifier_call(&p->notifier_list, NOTIFY_PEER_UP);
+ } else {
+ p->requested = p->fallback_requested = 0;
+ notifier_call(&p->notifier_list, NOTIFY_PEER_DOWN);
+ }
+ nhrp_peer_unref(p);
+ }
+ }
+}
+
+static void nhrp_peer_vc_notify(struct notifier_block *n, unsigned long cmd)
+{
+ struct nhrp_peer *p = container_of(n, struct nhrp_peer, vc_notifier);
+
+ switch (cmd) {
+ case NOTIFY_VC_IPSEC_CHANGED:
+ __nhrp_peer_check(p);
+ break;
+ case NOTIFY_VC_IPSEC_UPDATE_NBMA:
+ nhrp_peer_ref(p);
+ notifier_call(&p->notifier_list, NOTIFY_PEER_NBMA_CHANGING);
+ nhrp_peer_unref(p);
+ break;
+ }
+}
+
+static void nhrp_peer_ifp_notify(struct notifier_block *n, unsigned long cmd)
+{
+ struct nhrp_peer *p = container_of(n, struct nhrp_peer, ifp_notifier);
+ struct nhrp_interface *nifp;
+ struct nhrp_vc *vc;
+
+ nhrp_peer_ref(p);
+ switch (cmd) {
+ case NOTIFY_INTERFACE_UP:
+ case NOTIFY_INTERFACE_DOWN:
+ __nhrp_peer_check(p);
+ break;
+ case NOTIFY_INTERFACE_NBMA_CHANGED:
+ /* Source NBMA changed, rebind to new VC */
+ nifp = p->ifp->info;
+ vc = nhrp_vc_get(&nifp->nbma, &p->vc->remote.nbma, 1);
+ if (vc && p->vc != vc) {
+ nhrp_vc_notify_del(p->vc, &p->vc_notifier);
+ p->vc = vc;
+ nhrp_vc_notify_add(p->vc, &p->vc_notifier, nhrp_peer_vc_notify);
+ __nhrp_peer_check(p);
+ }
+ /* Fall-through to post config update */
+ case NOTIFY_INTERFACE_ADDRESS_CHANGED:
+ notifier_call(&p->notifier_list, NOTIFY_PEER_IFCONFIG_CHANGED);
+ break;
+ case NOTIFY_INTERFACE_MTU_CHANGED:
+ notifier_call(&p->notifier_list, NOTIFY_PEER_MTU_CHANGED);
+ break;
+ }
+ nhrp_peer_unref(p);
+}
+
+static unsigned int nhrp_peer_key(void *peer_data)
+{
+ struct nhrp_peer *p = peer_data;
+ return sockunion_hash(&p->vc->remote.nbma);
+}
+
+static int nhrp_peer_cmp(const void *cache_data, const void *key_data)
+{
+ const struct nhrp_peer *a = cache_data;
+ const struct nhrp_peer *b = key_data;
+ return a->ifp == b->ifp && a->vc == b->vc;
+}
+
+static void *nhrp_peer_create(void *data)
+{
+ struct nhrp_peer *p, *key = data;
+
+ p = XMALLOC(MTYPE_NHRP_PEER, sizeof(*p));
+ if (p) {
+ *p = (struct nhrp_peer) {
+ .ref = 0,
+ .ifp = key->ifp,
+ .vc = key->vc,
+ .notifier_list = NOTIFIER_LIST_INITIALIZER(&p->notifier_list),
+ };
+ nhrp_vc_notify_add(p->vc, &p->vc_notifier, nhrp_peer_vc_notify);
+ nhrp_interface_notify_add(p->ifp, &p->ifp_notifier, nhrp_peer_ifp_notify);
+ }
+ return p;
+}
+
+struct nhrp_peer *nhrp_peer_get(struct interface *ifp, const union sockunion *remote_nbma)
+{
+ struct nhrp_interface *nifp = ifp->info;
+ struct nhrp_peer key, *p;
+ struct nhrp_vc *vc;
+
+ if (!nifp->peer_hash) {
+ nifp->peer_hash = hash_create(nhrp_peer_key, nhrp_peer_cmp);
+ if (!nifp->peer_hash) return NULL;
+ }
+
+ vc = nhrp_vc_get(&nifp->nbma, remote_nbma, 1);
+ if (!vc) return NULL;
+
+ key.ifp = ifp;
+ key.vc = vc;
+
+ p = hash_get(nifp->peer_hash, &key, nhrp_peer_create);
+ nhrp_peer_ref(p);
+ if (p->ref == 1) __nhrp_peer_check(p);
+
+ return p;
+}
+
+struct nhrp_peer *nhrp_peer_ref(struct nhrp_peer *p)
+{
+ if (p) p->ref++;
+ return p;
+}
+
+void nhrp_peer_unref(struct nhrp_peer *p)
+{
+ if (p) {
+ p->ref--;
+ nhrp_peer_check_delete(p);
+ }
+}
+
+static int nhrp_peer_request_timeout(struct thread *t)
+{
+ struct nhrp_peer *p = THREAD_ARG(t);
+ struct nhrp_vc *vc = p->vc;
+ struct interface *ifp = p->ifp;
+ struct nhrp_interface *nifp = ifp->info;
+
+ p->t_fallback = NULL;
+
+ if (p->online)
+ return 0;
+
+ if (nifp->ipsec_fallback_profile && !p->prio && !p->fallback_requested) {
+ p->fallback_requested = 1;
+ vici_request_vc(nifp->ipsec_fallback_profile,
+ &vc->local.nbma, &vc->remote.nbma, p->prio);
+ THREAD_TIMER_ON(master, p->t_fallback, nhrp_peer_request_timeout, p, 30);
+ } else {
+ p->requested = p->fallback_requested = 0;
+ }
+
+ return 0;
+}
+
+int nhrp_peer_check(struct nhrp_peer *p, int establish)
+{
+ struct nhrp_vc *vc = p->vc;
+ struct interface *ifp = p->ifp;
+ struct nhrp_interface *nifp = ifp->info;
+
+ if (p->online)
+ return 1;
+ if (!establish)
+ return 0;
+ if (p->requested)
+ return 0;
+ if (sockunion_family(&vc->local.nbma) == AF_UNSPEC)
+ return 0;
+
+ p->prio = establish > 1;
+ p->requested = 1;
+ vici_request_vc(nifp->ipsec_profile, &vc->local.nbma, &vc->remote.nbma, p->prio);
+ THREAD_TIMER_ON(master, p->t_fallback, nhrp_peer_request_timeout, p,
+ (nifp->ipsec_fallback_profile && !p->prio) ? 15 : 30);
+
+ return 0;
+}
+
+void nhrp_peer_notify_add(struct nhrp_peer *p, struct notifier_block *n, notifier_fn_t fn)
+{
+ notifier_add(n, &p->notifier_list, fn);
+}
+
+void nhrp_peer_notify_del(struct nhrp_peer *p, struct notifier_block *n)
+{
+ notifier_del(n);
+ nhrp_peer_check_delete(p);
+}
+
+void nhrp_peer_send(struct nhrp_peer *p, struct zbuf *zb)
+{
+ char buf[2][256];
+
+ nhrp_packet_debug(zb, "Send");
+
+ if (!p->online)
+ return;
+
+ debugf(NHRP_DEBUG_KERNEL, "PACKET: Send %s -> %s",
+ sockunion2str(&p->vc->local.nbma, buf[0], sizeof buf[0]),
+ sockunion2str(&p->vc->remote.nbma, buf[1], sizeof buf[1]));
+
+ os_sendmsg(zb->head, zbuf_used(zb),
+ p->ifp->ifindex,
+ sockunion_get_addr(&p->vc->remote.nbma),
+ sockunion_get_addrlen(&p->vc->remote.nbma));
+ zbuf_reset(zb);
+}
+
+static void nhrp_handle_resolution_req(struct nhrp_packet_parser *p)
+{
+ struct zbuf *zb, payload;
+ struct nhrp_packet_header *hdr;
+ struct nhrp_cie_header *cie;
+ struct nhrp_extension_header *ext;
+ struct nhrp_interface *nifp;
+ struct nhrp_peer *peer;
+
+ if (!(p->if_ad->flags & NHRP_IFF_SHORTCUT)) {
+ debugf(NHRP_DEBUG_COMMON, "Shortcuts disabled");
+ /* FIXME: Send error indication? */
+ return;
+ }
+
+ if (p->if_ad->network_id &&
+ p->route_type == NHRP_ROUTE_OFF_NBMA &&
+ p->route_prefix.prefixlen < 8) {
+ debugf(NHRP_DEBUG_COMMON, "Shortcut to more generic than /8 dropped");
+ return;
+ }
+
+ debugf(NHRP_DEBUG_COMMON, "Parsing and replying to Resolution Req");
+
+ if (nhrp_route_address(p->ifp, &p->src_proto, NULL, &peer) != NHRP_ROUTE_NBMA_NEXTHOP)
+ return;
+
+#if 0
+ /* FIXME: Update requestors binding if CIE specifies holding time */
+ nhrp_cache_update_binding(
+ NHRP_CACHE_CACHED, &p->src_proto,
+ nhrp_peer_get(p->ifp, &p->src_nbma),
+ htons(cie->holding_time));
+#endif
+
+ nifp = peer->ifp->info;
+
+ /* Create reply */
+ zb = zbuf_alloc(1500);
+ hdr = nhrp_packet_push(zb, NHRP_PACKET_RESOLUTION_REPLY, &p->src_nbma, &p->src_proto, &p->dst_proto);
+
+ /* Copied information from request */
+ hdr->flags = p->hdr->flags & htons(NHRP_FLAG_RESOLUTION_SOURCE_IS_ROUTER|NHRP_FLAG_RESOLUTION_SOURCE_STABLE);
+ hdr->flags |= htons(NHRP_FLAG_RESOLUTION_DESTINATION_STABLE | NHRP_FLAG_RESOLUTION_AUTHORATIVE);
+ hdr->u.request_id = p->hdr->u.request_id;
+
+ /* CIE payload */
+ cie = nhrp_cie_push(zb, NHRP_CODE_SUCCESS, &nifp->nbma, &p->if_ad->addr);
+ cie->holding_time = htons(p->if_ad->holdtime);
+ cie->mtu = htons(p->if_ad->mtu);
+ if (p->if_ad->network_id && p->route_type == NHRP_ROUTE_OFF_NBMA)
+ cie->prefix_length = p->route_prefix.prefixlen;
+ else
+ cie->prefix_length = 8 * sockunion_get_addrlen(&p->if_ad->addr);
+
+ /* Handle extensions */
+ while ((ext = nhrp_ext_pull(&p->extensions, &payload)) != NULL) {
+ switch (htons(ext->type) & ~NHRP_EXTENSION_FLAG_COMPULSORY) {
+ case NHRP_EXTENSION_NAT_ADDRESS:
+ if (sockunion_family(&nifp->nat_nbma) == AF_UNSPEC)
+ break;
+ ext = nhrp_ext_push(zb, hdr, NHRP_EXTENSION_NAT_ADDRESS);
+ if (!ext) goto err;
+ cie = nhrp_cie_push(zb, NHRP_CODE_SUCCESS, &nifp->nat_nbma, &p->if_ad->addr);
+ if (!cie) goto err;
+ nhrp_ext_complete(zb, ext);
+ break;
+ default:
+ if (nhrp_ext_reply(zb, hdr, p->ifp, ext, &payload) < 0)
+ goto err;
+ break;
+ }
+ }
+
+ nhrp_packet_complete(zb, hdr);
+ nhrp_peer_send(peer, zb);
+err:
+ nhrp_peer_unref(peer);
+ zbuf_free(zb);
+}
+
+static void nhrp_handle_registration_request(struct nhrp_packet_parser *p)
+{
+ struct interface *ifp = p->ifp;
+ struct zbuf *zb, payload;
+ struct nhrp_packet_header *hdr;
+ struct nhrp_cie_header *cie;
+ struct nhrp_extension_header *ext;
+ struct nhrp_cache *c;
+ union sockunion cie_nbma, cie_proto, *proto_addr, *nbma_addr, *nbma_natoa;
+ int holdtime, natted = 0;
+ size_t paylen;
+ void *pay;
+
+ debugf(NHRP_DEBUG_COMMON, "Parsing and replying to Registration Req");
+
+ if (!sockunion_same(&p->src_nbma, &p->peer->vc->remote.nbma))
+ natted = 1;
+
+ /* Create reply */
+ zb = zbuf_alloc(1500);
+ hdr = nhrp_packet_push(zb, NHRP_PACKET_REGISTRATION_REPLY,
+ &p->src_nbma, &p->src_proto, &p->if_ad->addr);
+
+ /* Copied information from request */
+ hdr->flags = p->hdr->flags & htons(NHRP_FLAG_REGISTRATION_UNIQUE | NHRP_FLAG_REGISTRATION_NAT);
+ hdr->u.request_id = p->hdr->u.request_id;
+
+ /* Copy payload CIEs */
+ paylen = zbuf_used(&p->payload);
+ pay = zbuf_pushn(zb, paylen);
+ if (!pay) goto err;
+ memcpy(pay, zbuf_pulln(&p->payload, paylen), paylen);
+ zbuf_init(&payload, pay, paylen, paylen);
+
+ while ((cie = nhrp_cie_pull(&payload, hdr, &cie_nbma, &cie_proto)) != NULL) {
+ if (cie->prefix_length != 0xff && !(p->hdr->flags & htons(NHRP_FLAG_REGISTRATION_UNIQUE))) {
+ cie->code = NHRP_CODE_BINDING_NON_UNIQUE;
+ continue;
+ }
+
+ /* We currently support only unique prefix registrations */
+ if (cie->prefix_length != 0xff) {
+ cie->code = NHRP_CODE_ADMINISTRATIVELY_PROHIBITED;
+ continue;
+ }
+
+ proto_addr = (sockunion_family(&cie_proto) == AF_UNSPEC) ? &p->src_proto : &cie_proto;
+ nbma_addr = (sockunion_family(&cie_nbma) == AF_UNSPEC) ? &p->src_nbma : &cie_nbma;
+ nbma_natoa = NULL;
+ if (natted) {
+ nbma_natoa = nbma_addr;
+ nbma_addr = &p->peer->vc->remote.nbma;
+ }
+
+ holdtime = htons(cie->holding_time);
+ if (!holdtime) holdtime = p->if_ad->holdtime;
+
+ c = nhrp_cache_get(ifp, proto_addr, 1);
+ if (!c) {
+ cie->code = NHRP_CODE_INSUFFICIENT_RESOURCES;
+ continue;
+ }
+
+ if (!nhrp_cache_update_binding(c, NHRP_CACHE_DYNAMIC, holdtime, nhrp_peer_ref(p->peer), htons(cie->mtu), nbma_natoa)) {
+ cie->code = NHRP_CODE_ADMINISTRATIVELY_PROHIBITED;
+ continue;
+ }
+
+ cie->code = NHRP_CODE_SUCCESS;
+ }
+
+ /* Handle extensions */
+ while ((ext = nhrp_ext_pull(&p->extensions, &payload)) != NULL) {
+ switch (htons(ext->type) & ~NHRP_EXTENSION_FLAG_COMPULSORY) {
+ case NHRP_EXTENSION_NAT_ADDRESS:
+ ext = nhrp_ext_push(zb, hdr, NHRP_EXTENSION_NAT_ADDRESS);
+ if (!ext) goto err;
+ zbuf_copy(zb, &payload, zbuf_used(&payload));
+ if (natted) {
+ nhrp_cie_push(zb, NHRP_CODE_SUCCESS,
+ &p->peer->vc->remote.nbma,
+ &p->src_proto);
+ }
+ nhrp_ext_complete(zb, ext);
+ break;
+ default:
+ if (nhrp_ext_reply(zb, hdr, ifp, ext, &payload) < 0)
+ goto err;
+ break;
+ }
+ }
+
+ nhrp_packet_complete(zb, hdr);
+ nhrp_peer_send(p->peer, zb);
+err:
+ zbuf_free(zb);
+}
+
+static int parse_ether_packet(struct zbuf *zb, uint16_t protocol_type, union sockunion *src, union sockunion *dst)
+{
+ switch (protocol_type) {
+ case ETH_P_IP: {
+ struct iphdr *iph = zbuf_pull(zb, struct iphdr);
+ if (iph) {
+ if (src) sockunion_set(src, AF_INET, (uint8_t*) &iph->saddr, sizeof(iph->saddr));
+ if (dst) sockunion_set(dst, AF_INET, (uint8_t*) &iph->daddr, sizeof(iph->daddr));
+ }
+ }
+ break;
+ case ETH_P_IPV6: {
+ struct ipv6hdr *iph = zbuf_pull(zb, struct ipv6hdr);
+ if (iph) {
+ if (src) sockunion_set(src, AF_INET6, (uint8_t*) &iph->saddr, sizeof(iph->saddr));
+ if (dst) sockunion_set(dst, AF_INET6, (uint8_t*) &iph->daddr, sizeof(iph->daddr));
+ }
+ }
+ break;
+ default:
+ return 0;
+ }
+ return 1;
+}
+
+void nhrp_peer_send_indication(struct interface *ifp, uint16_t protocol_type, struct zbuf *pkt)
+{
+ union sockunion dst;
+ struct zbuf *zb, payload;
+ struct nhrp_interface *nifp = ifp->info;
+ struct nhrp_afi_data *if_ad;
+ struct nhrp_packet_header *hdr;
+ struct nhrp_peer *p;
+ char buf[2][SU_ADDRSTRLEN];
+
+ if (!nifp->enabled) return;
+
+ payload = *pkt;
+ if (!parse_ether_packet(&payload, protocol_type, &dst, NULL))
+ return;
+
+ if (nhrp_route_address(ifp, &dst, NULL, &p) != NHRP_ROUTE_NBMA_NEXTHOP)
+ return;
+
+ if_ad = &nifp->afi[family2afi(sockunion_family(&dst))];
+ if (!(if_ad->flags & NHRP_IFF_REDIRECT)) {
+ debugf(NHRP_DEBUG_COMMON, "Send Traffic Indication to %s about packet to %s ignored",
+ sockunion2str(&p->vc->remote.nbma, buf[0], sizeof buf[0]),
+ sockunion2str(&dst, buf[1], sizeof buf[1]));
+ return;
+ }
+
+ debugf(NHRP_DEBUG_COMMON, "Send Traffic Indication to %s (online=%d) about packet to %s",
+ sockunion2str(&p->vc->remote.nbma, buf[0], sizeof buf[0]),
+ p->online,
+ sockunion2str(&dst, buf[1], sizeof buf[1]));
+
+ /* Create reply */
+ zb = zbuf_alloc(1500);
+ hdr = nhrp_packet_push(zb, NHRP_PACKET_TRAFFIC_INDICATION, &nifp->nbma, &if_ad->addr, &dst);
+ hdr->hop_count = 0;
+
+ /* Payload is the packet causing indication */
+ zbuf_copy(zb, pkt, zbuf_used(pkt));
+ nhrp_packet_complete(zb, hdr);
+ nhrp_peer_send(p, zb);
+ nhrp_peer_unref(p);
+ zbuf_free(zb);
+}
+
+static void nhrp_handle_error_ind(struct nhrp_packet_parser *pp)
+{
+ struct zbuf origmsg = pp->payload;
+ struct nhrp_packet_header *hdr;
+ struct nhrp_reqid *reqid;
+ union sockunion src_nbma, src_proto, dst_proto;
+ char buf[2][SU_ADDRSTRLEN];
+
+ hdr = nhrp_packet_pull(&origmsg, &src_nbma, &src_proto, &dst_proto);
+ if (!hdr) return;
+
+ debugf(NHRP_DEBUG_COMMON, "Error Indication from %s about packet to %s ignored",
+ sockunion2str(&pp->src_proto, buf[0], sizeof buf[0]),
+ sockunion2str(&dst_proto, buf[1], sizeof buf[1]));
+
+ reqid = nhrp_reqid_lookup(&nhrp_packet_reqid, htonl(hdr->u.request_id));
+ if (reqid)
+ reqid->cb(reqid, pp);
+}
+
+static void nhrp_handle_traffic_ind(struct nhrp_packet_parser *p)
+{
+ union sockunion dst;
+ char buf[2][SU_ADDRSTRLEN];
+
+ if (!parse_ether_packet(&p->payload, htons(p->hdr->protocol_type), NULL, &dst))
+ return;
+
+ debugf(NHRP_DEBUG_COMMON, "Traffic Indication from %s about packet to %s: %s",
+ sockunion2str(&p->src_proto, buf[0], sizeof buf[0]),
+ sockunion2str(&dst, buf[1], sizeof buf[1]),
+ (p->if_ad->flags & NHRP_IFF_SHORTCUT) ? "trying shortcut" : "ignored");
+
+ if (p->if_ad->flags & NHRP_IFF_SHORTCUT)
+ nhrp_shortcut_initiate(&dst);
+}
+
+enum packet_type_t {
+ PACKET_UNKNOWN = 0,
+ PACKET_REQUEST,
+ PACKET_REPLY,
+ PACKET_INDICATION,
+};
+
+static struct {
+ enum packet_type_t type;
+ const char *name;
+ void (*handler)(struct nhrp_packet_parser *);
+} packet_types[] = {
+ [NHRP_PACKET_RESOLUTION_REQUEST] = {
+ .type = PACKET_REQUEST,
+ .name = "Resolution-Request",
+ .handler = nhrp_handle_resolution_req,
+ },
+ [NHRP_PACKET_RESOLUTION_REPLY] = {
+ .type = PACKET_REPLY,
+ .name = "Resolution-Reply",
+ },
+ [NHRP_PACKET_REGISTRATION_REQUEST] = {
+ .type = PACKET_REQUEST,
+ .name = "Registration-Request",
+ .handler = nhrp_handle_registration_request,
+ },
+ [NHRP_PACKET_REGISTRATION_REPLY] = {
+ .type = PACKET_REPLY,
+ .name = "Registration-Reply",
+ },
+ [NHRP_PACKET_PURGE_REQUEST] = {
+ .type = PACKET_REQUEST,
+ .name = "Purge-Request",
+ },
+ [NHRP_PACKET_PURGE_REPLY] = {
+ .type = PACKET_REPLY,
+ .name = "Purge-Reply",
+ },
+ [NHRP_PACKET_ERROR_INDICATION] = {
+ .type = PACKET_INDICATION,
+ .name = "Error-Indication",
+ .handler = nhrp_handle_error_ind,
+ },
+ [NHRP_PACKET_TRAFFIC_INDICATION] = {
+ .type = PACKET_INDICATION,
+ .name = "Traffic-Indication",
+ .handler = nhrp_handle_traffic_ind,
+ }
+};
+
+static void nhrp_peer_forward(struct nhrp_peer *p, struct nhrp_packet_parser *pp)
+{
+ struct zbuf *zb, extpl;
+ struct nhrp_packet_header *hdr;
+ struct nhrp_extension_header *ext, *dst;
+ struct nhrp_cie_header *cie;
+ struct nhrp_interface *nifp = pp->ifp->info;
+ struct nhrp_afi_data *if_ad = pp->if_ad;
+ union sockunion cie_nbma, cie_protocol;
+ uint16_t type, len;
+
+ if (pp->hdr->hop_count == 0)
+ return;
+
+ /* Create forward packet - copy header */
+ zb = zbuf_alloc(1500);
+ hdr = nhrp_packet_push(zb, pp->hdr->type, &pp->src_nbma, &pp->src_proto, &pp->dst_proto);
+ hdr->flags = pp->hdr->flags;
+ hdr->hop_count = pp->hdr->hop_count - 1;
+ hdr->u.request_id = pp->hdr->u.request_id;
+
+ /* Copy payload */
+ zbuf_copy(zb, &pp->payload, zbuf_used(&pp->payload));
+
+ /* Copy extensions */
+ while ((ext = nhrp_ext_pull(&pp->extensions, &extpl)) != NULL) {
+ type = htons(ext->type) & ~NHRP_EXTENSION_FLAG_COMPULSORY;
+ len = htons(ext->length);
+
+ if (type == NHRP_EXTENSION_END)
+ break;
+
+ dst = nhrp_ext_push(zb, hdr, htons(ext->type));
+ if (!dst) goto err;
+
+ switch (type) {
+ case NHRP_EXTENSION_FORWARD_TRANSIT_NHS:
+ case NHRP_EXTENSION_REVERSE_TRANSIT_NHS:
+ zbuf_put(zb, extpl.head, len);
+ if ((type == NHRP_EXTENSION_REVERSE_TRANSIT_NHS) ==
+ (packet_types[hdr->type].type == PACKET_REPLY)) {
+ /* Check NHS list for forwarding loop */
+ while ((cie = nhrp_cie_pull(&extpl, pp->hdr, &cie_nbma, &cie_protocol)) != NULL) {
+ if (sockunion_same(&p->vc->remote.nbma, &cie_nbma))
+ goto err;
+ }
+ /* Append our selves to the list */
+ cie = nhrp_cie_push(zb, NHRP_CODE_SUCCESS, &nifp->nbma, &if_ad->addr);
+ if (!cie) goto err;
+ cie->holding_time = htons(if_ad->holdtime);
+ }
+ break;
+ default:
+ if (htons(ext->type) & NHRP_EXTENSION_FLAG_COMPULSORY)
+ /* FIXME: RFC says to just copy, but not
+ * append our selves to the transit NHS list */
+ goto err;
+ case NHRP_EXTENSION_RESPONDER_ADDRESS:
+ /* Supported compulsory extensions, and any
+ * non-compulsory that is not explicitly handled,
+ * should be just copied. */
+ zbuf_copy(zb, &extpl, len);
+ break;
+ }
+ nhrp_ext_complete(zb, dst);
+ }
+
+ nhrp_packet_complete(zb, hdr);
+ nhrp_peer_send(p, zb);
+ zbuf_free(zb);
+ return;
+err:
+ nhrp_packet_debug(pp->pkt, "FWD-FAIL");
+ zbuf_free(zb);
+}
+
+static void nhrp_packet_debug(struct zbuf *zb, const char *dir)
+{
+ char buf[2][SU_ADDRSTRLEN];
+ union sockunion src_nbma, src_proto, dst_proto;
+ struct nhrp_packet_header *hdr;
+ struct zbuf zhdr;
+ int reply;
+
+ if (likely(!(debug_flags & NHRP_DEBUG_COMMON)))
+ return;
+
+ zbuf_init(&zhdr, zb->buf, zb->tail-zb->buf, zb->tail-zb->buf);
+ hdr = nhrp_packet_pull(&zhdr, &src_nbma, &src_proto, &dst_proto);
+
+ sockunion2str(&src_proto, buf[0], sizeof buf[0]);
+ sockunion2str(&dst_proto, buf[1], sizeof buf[1]);
+
+ reply = packet_types[hdr->type].type == PACKET_REPLY;
+ debugf(NHRP_DEBUG_COMMON, "%s %s(%d) %s -> %s",
+ dir,
+ packet_types[hdr->type].name ? : "Unknown",
+ hdr->type,
+ reply ? buf[1] : buf[0],
+ reply ? buf[0] : buf[1]);
+}
+
+struct nhrp_route_info {
+ int local;
+ struct interface *ifp;
+ struct nhrp_vc *vc;
+};
+
+void nhrp_peer_recv(struct nhrp_peer *p, struct zbuf *zb)
+{
+ char buf[2][SU_ADDRSTRLEN];
+ struct nhrp_packet_header *hdr;
+ struct nhrp_vc *vc = p->vc;
+ struct interface *ifp = p->ifp;
+ struct nhrp_interface *nifp = ifp->info;
+ struct nhrp_packet_parser pp;
+ struct nhrp_peer *peer = NULL;
+ struct nhrp_reqid *reqid;
+ const char *info = NULL;
+ union sockunion *target_addr;
+ unsigned paylen, extoff, extlen, realsize;
+ afi_t afi;
+
+ debugf(NHRP_DEBUG_KERNEL, "PACKET: Recv %s -> %s",
+ sockunion2str(&vc->remote.nbma, buf[0], sizeof buf[0]),
+ sockunion2str(&vc->local.nbma, buf[1], sizeof buf[1]));
+
+ if (!p->online) {
+ info = "peer not online";
+ goto drop;
+ }
+
+ if (nhrp_packet_calculate_checksum(zb->head, zbuf_used(zb)) != 0) {
+ info = "bad checksum";
+ goto drop;
+ }
+
+ realsize = zbuf_used(zb);
+ hdr = nhrp_packet_pull(zb, &pp.src_nbma, &pp.src_proto, &pp.dst_proto);
+ if (!hdr) {
+ info = "corrupt header";
+ goto drop;
+ }
+
+ pp.ifp = ifp;
+ pp.pkt = zb;
+ pp.hdr = hdr;
+ pp.peer = p;
+
+ afi = htons(hdr->afnum);
+ if (hdr->type > ZEBRA_NUM_OF(packet_types) ||
+ hdr->version != NHRP_VERSION_RFC2332 ||
+ afi >= AFI_MAX ||
+ packet_types[hdr->type].type == PACKET_UNKNOWN ||
+ htons(hdr->packet_size) > realsize) {
+ zlog_info("From %s: error: packet type %d, version %d, AFI %d, size %d (real size %d)",
+ sockunion2str(&vc->remote.nbma, buf[0], sizeof buf[0]),
+ (int) hdr->type, (int) hdr->version, (int) afi,
+ (int) htons(hdr->packet_size),
+ (int) realsize);
+ goto drop;
+ }
+ pp.if_ad = &((struct nhrp_interface *)ifp->info)->afi[afi];
+
+ extoff = htons(hdr->extension_offset);
+ if (extoff) {
+ if (extoff >= realsize) {
+ info = "extoff larger than packet";
+ goto drop;
+ }
+ paylen = extoff - (zb->head - zb->buf);
+ } else {
+ paylen = zbuf_used(zb);
+ }
+ zbuf_init(&pp.payload, zbuf_pulln(zb, paylen), paylen, paylen);
+ extlen = zbuf_used(zb);
+ zbuf_init(&pp.extensions, zbuf_pulln(zb, extlen), extlen, extlen);
+
+ if (!nifp->afi[afi].network_id) {
+ info = "nhrp not enabled";
+ goto drop;
+ }
+
+ nhrp_packet_debug(zb, "Recv");
+
+ /* FIXME: Check authentication here. This extension needs to be
+ * pre-handled. */
+
+ /* Figure out if this is local */
+ target_addr = (packet_types[hdr->type].type == PACKET_REPLY) ? &pp.src_proto : &pp.dst_proto;
+
+ if (sockunion_same(&pp.src_proto, &pp.dst_proto))
+ pp.route_type = NHRP_ROUTE_LOCAL;
+ else
+ pp.route_type = nhrp_route_address(pp.ifp, target_addr, &pp.route_prefix, &peer);
+
+ switch (pp.route_type) {
+ case NHRP_ROUTE_LOCAL:
+ nhrp_packet_debug(zb, "!LOCAL");
+ if (packet_types[hdr->type].type == PACKET_REPLY) {
+ reqid = nhrp_reqid_lookup(&nhrp_packet_reqid, htonl(hdr->u.request_id));
+ if (reqid) {
+ reqid->cb(reqid, &pp);
+ break;
+ } else {
+ nhrp_packet_debug(zb, "!UNKNOWN-REQID");
+ /* FIXME: send error-indication */
+ }
+ }
+ case NHRP_ROUTE_OFF_NBMA:
+ if (packet_types[hdr->type].handler) {
+ packet_types[hdr->type].handler(&pp);
+ break;
+ }
+ break;
+ case NHRP_ROUTE_NBMA_NEXTHOP:
+ nhrp_peer_forward(peer, &pp);
+ break;
+ case NHRP_ROUTE_BLACKHOLE:
+ break;
+ }
+
+drop:
+ if (info) {
+ zlog_info("From %s: error: %s",
+ sockunion2str(&vc->remote.nbma, buf[0], sizeof buf[0]),
+ info);
+ }
+ if (peer) nhrp_peer_unref(peer);
+ zbuf_free(zb);
+}
diff --git a/nhrpd/nhrp_protocol.h b/nhrpd/nhrp_protocol.h
new file mode 100644
index 0000000000..a4bc9fa6ba
--- /dev/null
+++ b/nhrpd/nhrp_protocol.h
@@ -0,0 +1,128 @@
+/* nhrp_protocol.h - NHRP protocol definitions
+ *
+ * Copyright (c) 2007-2012 Timo Teräs <timo.teras@iki.fi>
+ *
+ * This software is licensed under the MIT License.
+ * See MIT-LICENSE.txt for additional details.
+ */
+
+#ifndef NHRP_PROTOCOL_H
+#define NHRP_PROTOCOL_H
+
+#include <stdint.h>
+
+/* NHRP Ethernet protocol number */
+#define ETH_P_NHRP 0x2001
+
+/* NHRP Version */
+#define NHRP_VERSION_RFC2332 1
+
+/* NHRP Packet Types */
+#define NHRP_PACKET_RESOLUTION_REQUEST 1
+#define NHRP_PACKET_RESOLUTION_REPLY 2
+#define NHRP_PACKET_REGISTRATION_REQUEST 3
+#define NHRP_PACKET_REGISTRATION_REPLY 4
+#define NHRP_PACKET_PURGE_REQUEST 5
+#define NHRP_PACKET_PURGE_REPLY 6
+#define NHRP_PACKET_ERROR_INDICATION 7
+#define NHRP_PACKET_TRAFFIC_INDICATION 8
+
+/* NHRP Extension Types */
+#define NHRP_EXTENSION_FLAG_COMPULSORY 0x8000
+#define NHRP_EXTENSION_END 0
+#define NHRP_EXTENSION_PAYLOAD 0
+#define NHRP_EXTENSION_RESPONDER_ADDRESS 3
+#define NHRP_EXTENSION_FORWARD_TRANSIT_NHS 4
+#define NHRP_EXTENSION_REVERSE_TRANSIT_NHS 5
+#define NHRP_EXTENSION_AUTHENTICATION 7
+#define NHRP_EXTENSION_VENDOR 8
+#define NHRP_EXTENSION_NAT_ADDRESS 9
+
+/* NHRP Error Indication Codes */
+#define NHRP_ERROR_UNRECOGNIZED_EXTENSION 1
+#define NHRP_ERROR_LOOP_DETECTED 2
+#define NHRP_ERROR_PROTOCOL_ADDRESS_UNREACHABLE 6
+#define NHRP_ERROR_PROTOCOL_ERROR 7
+#define NHRP_ERROR_SDU_SIZE_EXCEEDED 8
+#define NHRP_ERROR_INVALID_EXTENSION 9
+#define NHRP_ERROR_INVALID_RESOLUTION_REPLY 10
+#define NHRP_ERROR_AUTHENTICATION_FAILURE 11
+#define NHRP_ERROR_HOP_COUNT_EXCEEDED 15
+
+/* NHRP CIE Codes */
+#define NHRP_CODE_SUCCESS 0
+#define NHRP_CODE_ADMINISTRATIVELY_PROHIBITED 4
+#define NHRP_CODE_INSUFFICIENT_RESOURCES 5
+#define NHRP_CODE_NO_BINDING_EXISTS 11
+#define NHRP_CODE_BINDING_NON_UNIQUE 13
+#define NHRP_CODE_UNIQUE_ADDRESS_REGISTERED 14
+
+/* NHRP Flags for Resolution request/reply */
+#define NHRP_FLAG_RESOLUTION_SOURCE_IS_ROUTER 0x8000
+#define NHRP_FLAG_RESOLUTION_AUTHORATIVE 0x4000
+#define NHRP_FLAG_RESOLUTION_DESTINATION_STABLE 0x2000
+#define NHRP_FLAG_RESOLUTION_UNIQUE 0x1000
+#define NHRP_FLAG_RESOLUTION_SOURCE_STABLE 0x0800
+#define NHRP_FLAG_RESOLUTION_NAT 0x0002
+
+/* NHRP Flags for Registration request/reply */
+#define NHRP_FLAG_REGISTRATION_UNIQUE 0x8000
+#define NHRP_FLAG_REGISTRATION_NAT 0x0002
+
+/* NHRP Flags for Purge request/reply */
+#define NHRP_FLAG_PURGE_NO_REPLY 0x8000
+
+/* NHRP Authentication extension types (ala Cisco) */
+#define NHRP_AUTHENTICATION_PLAINTEXT 0x00000001
+
+/* NHRP Packet Structures */
+struct nhrp_packet_header {
+ /* Fixed header */
+ uint16_t afnum;
+ uint16_t protocol_type;
+ uint8_t snap[5];
+ uint8_t hop_count;
+ uint16_t packet_size;
+ uint16_t checksum;
+ uint16_t extension_offset;
+ uint8_t version;
+ uint8_t type;
+ uint8_t src_nbma_address_len;
+ uint8_t src_nbma_subaddress_len;
+
+ /* Mandatory header */
+ uint8_t src_protocol_address_len;
+ uint8_t dst_protocol_address_len;
+ uint16_t flags;
+ union {
+ uint32_t request_id;
+ struct {
+ uint16_t code;
+ uint16_t offset;
+ } error;
+ } u;
+} __attribute__((packed));
+
+struct nhrp_cie_header {
+ uint8_t code;
+ uint8_t prefix_length;
+ uint16_t unused;
+ uint16_t mtu;
+ uint16_t holding_time;
+ uint8_t nbma_address_len;
+ uint8_t nbma_subaddress_len;
+ uint8_t protocol_address_len;
+ uint8_t preference;
+} __attribute__((packed));
+
+struct nhrp_extension_header {
+ uint16_t type;
+ uint16_t length;
+} __attribute__((packed));
+
+struct nhrp_cisco_authentication_extension {
+ uint32_t type;
+ uint8_t secret[8];
+} __attribute__((packed));
+
+#endif
diff --git a/nhrpd/nhrp_route.c b/nhrpd/nhrp_route.c
new file mode 100644
index 0000000000..8d066eeaef
--- /dev/null
+++ b/nhrpd/nhrp_route.c
@@ -0,0 +1,347 @@
+/* NHRP routing functions
+ * Copyright (c) 2014-2015 Timo Teräs
+ *
+ * This file is free software: you may copy, redistribute 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.
+ */
+
+#include "nhrpd.h"
+#include "table.h"
+#include "memory.h"
+#include "stream.h"
+#include "log.h"
+#include "zclient.h"
+
+DEFINE_MTYPE_STATIC(NHRPD, NHRP_ROUTE, "NHRP routing entry")
+
+static struct zclient *zclient;
+static struct route_table *zebra_rib[AFI_MAX];
+
+struct route_info {
+ union sockunion via;
+ struct interface *ifp;
+ struct interface *nhrp_ifp;
+};
+
+static struct route_node *nhrp_route_update_get(const struct prefix *p, int create)
+{
+ struct route_node *rn;
+ afi_t afi = family2afi(PREFIX_FAMILY(p));
+
+ if (!zebra_rib[afi])
+ return NULL;
+
+ if (create) {
+ rn = route_node_get(zebra_rib[afi], p);
+ if (!rn->info) {
+ rn->info = XCALLOC(MTYPE_NHRP_ROUTE, sizeof(struct route_info));
+ route_lock_node(rn);
+ }
+ return rn;
+ } else {
+ return route_node_lookup(zebra_rib[afi], p);
+ }
+}
+
+static void nhrp_route_update_put(struct route_node *rn)
+{
+ struct route_info *ri = rn->info;
+
+ if (!ri->ifp && !ri->nhrp_ifp && sockunion_family(&ri->via) == AF_UNSPEC) {
+ XFREE(MTYPE_NHRP_ROUTE, rn->info);
+ rn->info = NULL;
+ route_unlock_node(rn);
+ }
+ route_unlock_node(rn);
+}
+
+static void nhrp_route_update_zebra(const struct prefix *p, union sockunion *nexthop, struct interface *ifp)
+{
+ struct route_node *rn;
+ struct route_info *ri;
+
+ rn = nhrp_route_update_get(p, (sockunion_family(nexthop) != AF_UNSPEC) || ifp);
+ if (rn) {
+ ri = rn->info;
+ ri->via = *nexthop;
+ ri->ifp = ifp;
+ nhrp_route_update_put(rn);
+ }
+}
+
+void nhrp_route_update_nhrp(const struct prefix *p, struct interface *ifp)
+{
+ struct route_node *rn;
+ struct route_info *ri;
+
+ rn = nhrp_route_update_get(p, ifp != NULL);
+ if (rn) {
+ ri = rn->info;
+ ri->nhrp_ifp = ifp;
+ nhrp_route_update_put(rn);
+ }
+}
+
+void nhrp_route_announce(int add, enum nhrp_cache_type type, const struct prefix *p, struct interface *ifp, const union sockunion *nexthop, uint32_t mtu)
+{
+ struct in_addr *nexthop_ipv4;
+ int flags = 0;
+
+ if (zclient->sock < 0)
+ return;
+
+ switch (type) {
+ case NHRP_CACHE_NEGATIVE:
+ SET_FLAG(flags, ZEBRA_FLAG_REJECT);
+ break;
+ case NHRP_CACHE_DYNAMIC:
+ case NHRP_CACHE_NHS:
+ case NHRP_CACHE_STATIC:
+ /* Regular route, so these are announced
+ * to other routing daemons */
+ break;
+ default:
+ SET_FLAG(flags, ZEBRA_FLAG_FIB_OVERRIDE);
+ break;
+ }
+ SET_FLAG(flags, ZEBRA_FLAG_INTERNAL);
+
+ if (p->family == AF_INET) {
+ struct zapi_ipv4 api;
+
+ memset(&api, 0, sizeof(api));
+ api.flags = flags;
+ api.type = ZEBRA_ROUTE_NHRP;
+ api.safi = SAFI_UNICAST;
+
+ SET_FLAG(api.message, ZAPI_MESSAGE_NEXTHOP);
+ if (nexthop) {
+ SET_FLAG(api.message, ZAPI_MESSAGE_NEXTHOP);
+ nexthop_ipv4 = (struct in_addr *) sockunion_get_addr(nexthop);
+ api.nexthop_num = 1;
+ api.nexthop = &nexthop_ipv4;
+ }
+ if (ifp) {
+ SET_FLAG(api.message, ZAPI_MESSAGE_IFINDEX);
+ api.ifindex_num = 1;
+ api.ifindex = &ifp->ifindex;
+ }
+ if (mtu) {
+ SET_FLAG(api.message, ZAPI_MESSAGE_MTU);
+ api.mtu = mtu;
+ }
+
+ if (unlikely(debug_flags & NHRP_DEBUG_ROUTE)) {
+ char buf[2][INET_ADDRSTRLEN];
+ zlog_debug("Zebra send: IPv4 route %s %s/%d nexthop %s metric %u"
+ " count %d dev %s",
+ add ? "add" : "del",
+ inet_ntop(AF_INET, &p->u.prefix4, buf[0], sizeof(buf[0])),
+ p->prefixlen,
+ nexthop ? inet_ntop(AF_INET, api.nexthop[0], buf[1], sizeof(buf[1])) : "<onlink>",
+ api.metric, api.nexthop_num, ifp->name);
+ }
+
+ zapi_ipv4_route(
+ add ? ZEBRA_IPV4_ROUTE_ADD : ZEBRA_IPV4_ROUTE_DELETE,
+ zclient, (struct prefix_ipv4 *) p, &api);
+ }
+}
+
+int nhrp_route_read(int cmd, struct zclient *zclient, zebra_size_t length, vrf_id_t vrf_id)
+{
+ struct stream *s;
+ struct interface *ifp = NULL;
+ struct prefix prefix;
+ union sockunion nexthop_addr;
+ unsigned char message, nexthop_num, ifindex_num;
+ unsigned ifindex;
+ char buf[2][PREFIX_STRLEN];
+ int i, afaddrlen, added;
+
+ s = zclient->ibuf;
+ memset(&prefix, 0, sizeof(prefix));
+ sockunion_family(&nexthop_addr) = AF_UNSPEC;
+
+ /* Type, flags, message. */
+ /*type =*/ stream_getc(s);
+ /*flags =*/ stream_getc(s);
+ message = stream_getc(s);
+
+ /* Prefix */
+ switch (cmd) {
+ case ZEBRA_IPV4_ROUTE_ADD:
+ case ZEBRA_IPV4_ROUTE_DELETE:
+ prefix.family = AF_INET;
+ break;
+ case ZEBRA_IPV6_ROUTE_ADD:
+ case ZEBRA_IPV6_ROUTE_DELETE:
+ prefix.family = AF_INET6;
+ break;
+ default:
+ return -1;
+ }
+ afaddrlen = family2addrsize(prefix.family);
+ prefix.prefixlen = stream_getc(s);
+ stream_get(&prefix.u.val, s, PSIZE(prefix.prefixlen));
+
+ /* Nexthop, ifindex, distance, metric. */
+ if (CHECK_FLAG(message, ZAPI_MESSAGE_NEXTHOP|ZAPI_MESSAGE_IFINDEX)) {
+ nexthop_num = stream_getc(s);
+ for (i = 0; i < nexthop_num; i++) {
+ stream_get(buf[0], s, afaddrlen);
+ if (i == 0) sockunion_set(&nexthop_addr, prefix.family, (u_char*) buf[0], afaddrlen);
+ }
+ ifindex_num = stream_getc(s);
+ for (i = 0; i < ifindex_num; i++) {
+ ifindex = stream_getl(s);
+ if (i == 0 && ifindex != IFINDEX_INTERNAL)
+ ifp = if_lookup_by_index(ifindex);
+ }
+ }
+ if (CHECK_FLAG(message, ZAPI_MESSAGE_DISTANCE))
+ /*distance =*/ stream_getc(s);
+ if (CHECK_FLAG(message, ZAPI_MESSAGE_METRIC))
+ /*metric =*/ stream_getl(s);
+
+ added = (cmd == ZEBRA_IPV4_ROUTE_ADD || cmd == ZEBRA_IPV6_ROUTE_ADD);
+ debugf(NHRP_DEBUG_ROUTE, "if-route-%s: %s via %s dev %s",
+ added ? "add" : "del",
+ prefix2str(&prefix, buf[0], sizeof buf[0]),
+ sockunion2str(&nexthop_addr, buf[1], sizeof buf[1]),
+ ifp ? ifp->name : "(none)");
+
+ nhrp_route_update_zebra(&prefix, &nexthop_addr, ifp);
+ nhrp_shortcut_prefix_change(&prefix, !added);
+
+ return 0;
+}
+
+int nhrp_route_get_nexthop(const union sockunion *addr, struct prefix *p, union sockunion *via, struct interface **ifp)
+{
+ struct route_node *rn;
+ struct route_info *ri;
+ struct prefix lookup;
+ afi_t afi = family2afi(sockunion_family(addr));
+ char buf[PREFIX_STRLEN];
+
+ sockunion2hostprefix(addr, &lookup);
+
+ rn = route_node_match(zebra_rib[afi], &lookup);
+ if (!rn) return 0;
+
+ ri = rn->info;
+ if (ri->nhrp_ifp) {
+ debugf(NHRP_DEBUG_ROUTE, "lookup %s: nhrp_if=%s",
+ prefix2str(&lookup, buf, sizeof buf),
+ ri->nhrp_ifp->name);
+
+ if (via) sockunion_family(via) = AF_UNSPEC;
+ if (ifp) *ifp = ri->nhrp_ifp;
+ } else {
+ debugf(NHRP_DEBUG_ROUTE, "lookup %s: zebra route dev %s",
+ prefix2str(&lookup, buf, sizeof buf),
+ ri->ifp ? ri->ifp->name : "(none)");
+
+ if (via) *via = ri->via;
+ if (ifp) *ifp = ri->ifp;
+ }
+ if (p) *p = rn->p;
+ route_unlock_node(rn);
+ return 1;
+}
+
+enum nhrp_route_type nhrp_route_address(struct interface *in_ifp, union sockunion *addr, struct prefix *p, struct nhrp_peer **peer)
+{
+ struct interface *ifp = in_ifp;
+ struct nhrp_interface *nifp;
+ struct nhrp_cache *c;
+ union sockunion via[4];
+ uint32_t network_id = 0;
+ afi_t afi = family2afi(sockunion_family(addr));
+ int i;
+
+ if (ifp) {
+ nifp = ifp->info;
+ network_id = nifp->afi[afi].network_id;
+
+ c = nhrp_cache_get(ifp, addr, 0);
+ if (c && c->cur.type == NHRP_CACHE_LOCAL) {
+ if (p) memset(p, 0, sizeof(*p));
+ return NHRP_ROUTE_LOCAL;
+ }
+ }
+
+ for (i = 0; i < 4; i++) {
+ if (!nhrp_route_get_nexthop(addr, p, &via[i], &ifp))
+ return NHRP_ROUTE_BLACKHOLE;
+ if (ifp) {
+ /* Departing from nbma network? */
+ nifp = ifp->info;
+ if (network_id && network_id != nifp->afi[afi].network_id)
+ return NHRP_ROUTE_OFF_NBMA;
+ }
+ if (sockunion_family(&via[i]) == AF_UNSPEC)
+ break;
+ /* Resolve via node, but return the prefix of first match */
+ addr = &via[i];
+ p = NULL;
+ }
+
+ if (ifp) {
+ c = nhrp_cache_get(ifp, addr, 0);
+ if (c && c->cur.type >= NHRP_CACHE_DYNAMIC) {
+ if (p) memset(p, 0, sizeof(*p));
+ if (c->cur.type == NHRP_CACHE_LOCAL)
+ return NHRP_ROUTE_LOCAL;
+ if (peer) *peer = nhrp_peer_ref(c->cur.peer);
+ return NHRP_ROUTE_NBMA_NEXTHOP;
+ }
+ }
+
+ return NHRP_ROUTE_BLACKHOLE;
+}
+
+void nhrp_zebra_init(void)
+{
+ zebra_rib[AFI_IP] = route_table_init();
+ zebra_rib[AFI_IP6] = route_table_init();
+
+ zclient = zclient_new(master);
+ zclient->interface_add = nhrp_interface_add;
+ zclient->interface_delete = nhrp_interface_delete;
+ zclient->interface_up = nhrp_interface_up;
+ zclient->interface_down = nhrp_interface_down;
+ zclient->interface_address_add = nhrp_interface_address_add;
+ zclient->interface_address_delete = nhrp_interface_address_delete;
+ zclient->redistribute_route_ipv4_add = nhrp_route_read;
+ zclient->redistribute_route_ipv4_del = nhrp_route_read;
+ zclient->redistribute_route_ipv6_add = nhrp_route_read;
+ zclient->redistribute_route_ipv6_del = nhrp_route_read;
+
+ zclient_init(zclient, ZEBRA_ROUTE_NHRP, 0);
+ zclient_redistribute(ZEBRA_REDISTRIBUTE_ADD, zclient, AFI_IP, ZEBRA_ROUTE_KERNEL, 0, VRF_DEFAULT);
+ zclient_redistribute(ZEBRA_REDISTRIBUTE_ADD, zclient, AFI_IP, ZEBRA_ROUTE_CONNECT, 0, VRF_DEFAULT);
+ zclient_redistribute(ZEBRA_REDISTRIBUTE_ADD, zclient, AFI_IP, ZEBRA_ROUTE_STATIC, 0, VRF_DEFAULT);
+ zclient_redistribute(ZEBRA_REDISTRIBUTE_ADD, zclient, AFI_IP, ZEBRA_ROUTE_RIP, 0, VRF_DEFAULT);
+ zclient_redistribute(ZEBRA_REDISTRIBUTE_ADD, zclient, AFI_IP, ZEBRA_ROUTE_OSPF, 0, VRF_DEFAULT);
+ zclient_redistribute(ZEBRA_REDISTRIBUTE_ADD, zclient, AFI_IP, ZEBRA_ROUTE_ISIS, 0, VRF_DEFAULT);
+ zclient_redistribute(ZEBRA_REDISTRIBUTE_ADD, zclient, AFI_IP, ZEBRA_ROUTE_BGP, 0, VRF_DEFAULT);
+ zclient_redistribute(ZEBRA_REDISTRIBUTE_ADD, zclient, AFI_IP6, ZEBRA_ROUTE_KERNEL, 0, VRF_DEFAULT);
+ zclient_redistribute(ZEBRA_REDISTRIBUTE_ADD, zclient, AFI_IP6, ZEBRA_ROUTE_CONNECT, 0, VRF_DEFAULT);
+ zclient_redistribute(ZEBRA_REDISTRIBUTE_ADD, zclient, AFI_IP6, ZEBRA_ROUTE_STATIC, 0, VRF_DEFAULT);
+ zclient_redistribute(ZEBRA_REDISTRIBUTE_ADD, zclient, AFI_IP6, ZEBRA_ROUTE_RIP, 0, VRF_DEFAULT);
+ zclient_redistribute(ZEBRA_REDISTRIBUTE_ADD, zclient, AFI_IP6, ZEBRA_ROUTE_OSPF, 0, VRF_DEFAULT);
+ zclient_redistribute(ZEBRA_REDISTRIBUTE_ADD, zclient, AFI_IP6, ZEBRA_ROUTE_ISIS, 0, VRF_DEFAULT);
+ zclient_redistribute(ZEBRA_REDISTRIBUTE_ADD, zclient, AFI_IP6, ZEBRA_ROUTE_BGP, 0, VRF_DEFAULT);
+}
+
+void nhrp_zebra_terminate(void)
+{
+ zclient_stop(zclient);
+ route_table_finish(zebra_rib[AFI_IP]);
+ route_table_finish(zebra_rib[AFI_IP6]);
+}
+
diff --git a/nhrpd/nhrp_shortcut.c b/nhrpd/nhrp_shortcut.c
new file mode 100644
index 0000000000..cd33ff0280
--- /dev/null
+++ b/nhrpd/nhrp_shortcut.c
@@ -0,0 +1,404 @@
+/* NHRP shortcut related functions
+ * Copyright (c) 2014-2015 Timo Teräs
+ *
+ * This file is free software: you may copy, redistribute 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.
+ */
+
+#include "nhrpd.h"
+#include "table.h"
+#include "memory.h"
+#include "thread.h"
+#include "log.h"
+#include "nhrp_protocol.h"
+
+DEFINE_MTYPE_STATIC(NHRPD, NHRP_SHORTCUT, "NHRP shortcut")
+
+static struct route_table *shortcut_rib[AFI_MAX];
+
+static int nhrp_shortcut_do_purge(struct thread *t);
+static void nhrp_shortcut_delete(struct nhrp_shortcut *s);
+static void nhrp_shortcut_send_resolution_req(struct nhrp_shortcut *s);
+
+static void nhrp_shortcut_check_use(struct nhrp_shortcut *s)
+{
+ char buf[PREFIX_STRLEN];
+
+ if (s->expiring && s->cache && s->cache->used) {
+ debugf(NHRP_DEBUG_ROUTE, "Shortcut %s used and expiring",
+ prefix2str(s->p, buf, sizeof buf));
+ nhrp_shortcut_send_resolution_req(s);
+ }
+}
+
+static int nhrp_shortcut_do_expire(struct thread *t)
+{
+ struct nhrp_shortcut *s = THREAD_ARG(t);
+
+ s->t_timer = NULL;
+ THREAD_TIMER_ON(master, s->t_timer, nhrp_shortcut_do_purge, s, s->holding_time/3);
+ s->expiring = 1;
+ nhrp_shortcut_check_use(s);
+
+ return 0;
+}
+
+static void nhrp_shortcut_cache_notify(struct notifier_block *n, unsigned long cmd)
+{
+ struct nhrp_shortcut *s = container_of(n, struct nhrp_shortcut, cache_notifier);
+
+ switch (cmd) {
+ case NOTIFY_CACHE_UP:
+ if (!s->route_installed) {
+ nhrp_route_announce(1, s->type, s->p, NULL, &s->cache->remote_addr, 0);
+ s->route_installed = 1;
+ }
+ break;
+ case NOTIFY_CACHE_USED:
+ nhrp_shortcut_check_use(s);
+ break;
+ case NOTIFY_CACHE_DOWN:
+ case NOTIFY_CACHE_DELETE:
+ if (s->route_installed) {
+ nhrp_route_announce(0, NHRP_CACHE_INVALID, s->p, NULL, NULL, 0);
+ s->route_installed = 0;
+ }
+ if (cmd == NOTIFY_CACHE_DELETE)
+ nhrp_shortcut_delete(s);
+ break;
+ }
+}
+
+static void nhrp_shortcut_update_binding(struct nhrp_shortcut *s, enum nhrp_cache_type type, struct nhrp_cache *c, int holding_time)
+{
+ s->type = type;
+ if (c != s->cache) {
+ if (s->cache) {
+ nhrp_cache_notify_del(s->cache, &s->cache_notifier);
+ s->cache = NULL;
+ }
+ s->cache = c;
+ if (s->cache) {
+ nhrp_cache_notify_add(s->cache, &s->cache_notifier, nhrp_shortcut_cache_notify);
+ if (s->cache->route_installed) {
+ /* Force renewal of Zebra announce on prefix change */
+ s->route_installed = 0;
+ nhrp_shortcut_cache_notify(&s->cache_notifier, NOTIFY_CACHE_UP);
+ }
+ }
+ if (!s->cache || !s->cache->route_installed)
+ nhrp_shortcut_cache_notify(&s->cache_notifier, NOTIFY_CACHE_DOWN);
+ }
+ if (s->type == NHRP_CACHE_NEGATIVE && !s->route_installed) {
+ nhrp_route_announce(1, s->type, s->p, NULL, NULL, 0);
+ s->route_installed = 1;
+ } else if (s->type == NHRP_CACHE_INVALID && s->route_installed) {
+ nhrp_route_announce(0, NHRP_CACHE_INVALID, s->p, NULL, NULL, 0);
+ s->route_installed = 0;
+ }
+
+ THREAD_OFF(s->t_timer);
+ if (holding_time) {
+ s->expiring = 0;
+ s->holding_time = holding_time;
+ THREAD_TIMER_ON(master, s->t_timer, nhrp_shortcut_do_expire, s, 2*holding_time/3);
+ }
+}
+
+static void nhrp_shortcut_delete(struct nhrp_shortcut *s)
+{
+ struct route_node *rn;
+ afi_t afi = family2afi(PREFIX_FAMILY(s->p));
+ char buf[PREFIX_STRLEN];
+
+ THREAD_OFF(s->t_timer);
+ nhrp_reqid_free(&nhrp_packet_reqid, &s->reqid);
+
+ debugf(NHRP_DEBUG_ROUTE, "Shortcut %s purged",
+ prefix2str(s->p, buf, sizeof buf));
+
+ nhrp_shortcut_update_binding(s, NHRP_CACHE_INVALID, NULL, 0);
+
+ /* Delete node */
+ rn = route_node_lookup(shortcut_rib[afi], s->p);
+ if (rn) {
+ XFREE(MTYPE_NHRP_SHORTCUT, rn->info);
+ rn->info = NULL;
+ route_unlock_node(rn);
+ route_unlock_node(rn);
+ }
+}
+
+static int nhrp_shortcut_do_purge(struct thread *t)
+{
+ struct nhrp_shortcut *s = THREAD_ARG(t);
+ s->t_timer = NULL;
+ nhrp_shortcut_delete(s);
+ return 0;
+}
+
+static struct nhrp_shortcut *nhrp_shortcut_get(struct prefix *p)
+{
+ struct nhrp_shortcut *s;
+ struct route_node *rn;
+ char buf[PREFIX_STRLEN];
+ afi_t afi = family2afi(PREFIX_FAMILY(p));
+
+ if (!shortcut_rib[afi])
+ return 0;
+
+ rn = route_node_get(shortcut_rib[afi], p);
+ if (!rn->info) {
+ s = rn->info = XCALLOC(MTYPE_NHRP_SHORTCUT, sizeof(struct nhrp_shortcut));
+ s->type = NHRP_CACHE_INVALID;
+ s->p = &rn->p;
+
+ debugf(NHRP_DEBUG_ROUTE, "Shortcut %s created",
+ prefix2str(s->p, buf, sizeof buf));
+ } else {
+ s = rn->info;
+ route_unlock_node(rn);
+ }
+ return s;
+}
+
+static void nhrp_shortcut_recv_resolution_rep(struct nhrp_reqid *reqid, void *arg)
+{
+ struct nhrp_packet_parser *pp = arg;
+ struct nhrp_shortcut *s = container_of(reqid, struct nhrp_shortcut, reqid);
+ struct nhrp_shortcut *ps;
+ struct nhrp_extension_header *ext;
+ struct nhrp_cie_header *cie;
+ struct nhrp_cache *c = NULL;
+ union sockunion *proto, cie_proto, *nbma, *nbma_natoa, cie_nbma, nat_nbma;
+ struct prefix prefix, route_prefix;
+ struct zbuf extpl;
+ char bufp[PREFIX_STRLEN], buf[3][SU_ADDRSTRLEN];
+ int holding_time = pp->if_ad->holdtime;
+
+ nhrp_reqid_free(&nhrp_packet_reqid, &s->reqid);
+ THREAD_OFF(s->t_timer);
+ THREAD_TIMER_ON(master, s->t_timer, nhrp_shortcut_do_purge, s, 1);
+
+ if (pp->hdr->type != NHRP_PACKET_RESOLUTION_REPLY) {
+ if (pp->hdr->type == NHRP_PACKET_ERROR_INDICATION &&
+ pp->hdr->u.error.code == NHRP_ERROR_PROTOCOL_ADDRESS_UNREACHABLE) {
+ debugf(NHRP_DEBUG_COMMON, "Shortcut: Resolution: Protocol address unreachable");
+ nhrp_shortcut_update_binding(s, NHRP_CACHE_NEGATIVE, NULL, holding_time);
+ } else {
+ debugf(NHRP_DEBUG_COMMON, "Shortcut: Resolution failed");
+ }
+ return;
+ }
+
+ /* Parse extensions */
+ memset(&nat_nbma, 0, sizeof nat_nbma);
+ while ((ext = nhrp_ext_pull(&pp->extensions, &extpl)) != NULL) {
+ switch (htons(ext->type) & ~NHRP_EXTENSION_FLAG_COMPULSORY) {
+ case NHRP_EXTENSION_NAT_ADDRESS:
+ nhrp_cie_pull(&extpl, pp->hdr, &nat_nbma, &cie_proto);
+ break;
+ }
+ }
+
+ /* Minor sanity check */
+ prefix2sockunion(s->p, &cie_proto);
+ if (!sockunion_same(&cie_proto, &pp->dst_proto)) {
+ debugf(NHRP_DEBUG_COMMON, "Shortcut: Warning dst_proto altered from %s to %s",
+ sockunion2str(&cie_proto, buf[0], sizeof buf[0]),
+ sockunion2str(&pp->dst_proto, buf[1], sizeof buf[1]));
+ }
+
+ /* One or more CIEs should be given as reply, we support only one */
+ cie = nhrp_cie_pull(&pp->payload, pp->hdr, &cie_nbma, &cie_proto);
+ if (!cie || cie->code != NHRP_CODE_SUCCESS) {
+ debugf(NHRP_DEBUG_COMMON, "Shortcut: CIE code %d", cie ? cie->code : -1);
+ return;
+ }
+
+ proto = sockunion_family(&cie_proto) != AF_UNSPEC ? &cie_proto : &pp->dst_proto;
+ if (cie->holding_time)
+ holding_time = htons(cie->holding_time);
+
+ prefix = *s->p;
+ prefix.prefixlen = cie->prefix_length;
+
+ /* Sanity check prefix length */
+ if (prefix.prefixlen >= 8*prefix_blen(&prefix)) {
+ prefix.prefixlen = 8*prefix_blen(&prefix);
+ } else if (nhrp_route_address(NULL, &pp->dst_proto, &route_prefix, NULL) == NHRP_ROUTE_NBMA_NEXTHOP) {
+ if (prefix.prefixlen < route_prefix.prefixlen)
+ prefix.prefixlen = route_prefix.prefixlen;
+ }
+
+ debugf(NHRP_DEBUG_COMMON, "Shortcut: %s is at proto %s cie-nbma %s nat-nbma %s cie-holdtime %d",
+ prefix2str(&prefix, bufp, sizeof bufp),
+ sockunion2str(proto, buf[0], sizeof buf[0]),
+ sockunion2str(&cie_nbma, buf[1], sizeof buf[1]),
+ sockunion2str(&nat_nbma, buf[2], sizeof buf[2]),
+ htons(cie->holding_time));
+
+ /* Update cache entry for the protocol to nbma binding */
+ if (sockunion_family(&nat_nbma) != AF_UNSPEC) {
+ nbma = &nat_nbma;
+ nbma_natoa = &cie_nbma;
+ } else {
+ nbma = &cie_nbma;
+ nbma_natoa = NULL;
+ }
+ if (sockunion_family(nbma)) {
+ c = nhrp_cache_get(pp->ifp, proto, 1);
+ if (c) {
+ nhrp_cache_update_binding(
+ c, NHRP_CACHE_CACHED, holding_time,
+ nhrp_peer_get(pp->ifp, nbma),
+ htons(cie->mtu), nbma_natoa);
+ }
+ }
+
+ /* Update shortcut entry for subnet to protocol gw binding */
+ if (c && !sockunion_same(proto, &pp->dst_proto)) {
+ ps = nhrp_shortcut_get(&prefix);
+ if (ps) {
+ ps->addr = s->addr;
+ nhrp_shortcut_update_binding(ps, NHRP_CACHE_CACHED, c, holding_time);
+ }
+ }
+
+ debugf(NHRP_DEBUG_COMMON, "Shortcut: Resolution reply handled");
+}
+
+static void nhrp_shortcut_send_resolution_req(struct nhrp_shortcut *s)
+{
+ struct zbuf *zb;
+ struct nhrp_packet_header *hdr;
+ struct interface *ifp;
+ struct nhrp_interface *nifp;
+ struct nhrp_peer *peer;
+
+ if (nhrp_route_address(NULL, &s->addr, NULL, &peer) != NHRP_ROUTE_NBMA_NEXTHOP)
+ return;
+
+ if (s->type == NHRP_CACHE_INVALID || s->type == NHRP_CACHE_NEGATIVE)
+ s->type = NHRP_CACHE_INCOMPLETE;
+
+ ifp = peer->ifp;
+ nifp = ifp->info;
+
+ /* Create request */
+ zb = zbuf_alloc(1500);
+ hdr = nhrp_packet_push(zb, NHRP_PACKET_RESOLUTION_REQUEST,
+ &nifp->nbma, &nifp->afi[family2afi(sockunion_family(&s->addr))].addr, &s->addr);
+ hdr->u.request_id = htonl(nhrp_reqid_alloc(&nhrp_packet_reqid, &s->reqid, nhrp_shortcut_recv_resolution_rep));
+ hdr->flags = htons(NHRP_FLAG_RESOLUTION_SOURCE_IS_ROUTER |
+ NHRP_FLAG_RESOLUTION_AUTHORATIVE |
+ NHRP_FLAG_RESOLUTION_SOURCE_STABLE);
+
+ /* RFC2332 - One or zero CIEs, if CIE is present contains:
+ * - Prefix length: widest acceptable prefix we accept (if U set, 0xff)
+ * - MTU: MTU of the source station
+ * - Holding Time: Max time to cache the source information
+ * */
+ /* FIXME: Send holding time, and MTU */
+
+ nhrp_ext_request(zb, hdr, ifp);
+
+ /* Cisco NAT detection extension */
+ hdr->flags |= htons(NHRP_FLAG_RESOLUTION_NAT);
+ nhrp_ext_push(zb, hdr, NHRP_EXTENSION_NAT_ADDRESS);
+
+ nhrp_packet_complete(zb, hdr);
+
+ nhrp_peer_send(peer, zb);
+ nhrp_peer_unref(peer);
+ zbuf_free(zb);
+}
+
+void nhrp_shortcut_initiate(union sockunion *addr)
+{
+ struct prefix p;
+ struct nhrp_shortcut *s;
+
+ sockunion2hostprefix(addr, &p);
+ s = nhrp_shortcut_get(&p);
+ if (s && s->type != NHRP_CACHE_INCOMPLETE) {
+ s->addr = *addr;
+ THREAD_OFF(s->t_timer);
+ THREAD_TIMER_ON(master, s->t_timer, nhrp_shortcut_do_purge, s, 30);
+ nhrp_shortcut_send_resolution_req(s);
+ }
+}
+
+void nhrp_shortcut_init(void)
+{
+ shortcut_rib[AFI_IP] = route_table_init();
+ shortcut_rib[AFI_IP6] = route_table_init();
+}
+
+void nhrp_shortcut_terminate(void)
+{
+ route_table_finish(shortcut_rib[AFI_IP]);
+ route_table_finish(shortcut_rib[AFI_IP6]);
+}
+
+void nhrp_shortcut_foreach(afi_t afi, void (*cb)(struct nhrp_shortcut *, void *), void *ctx)
+{
+ struct route_table *rt = shortcut_rib[afi];
+ struct route_node *rn;
+ route_table_iter_t iter;
+
+ if (!rt) return;
+
+ route_table_iter_init(&iter, rt);
+ while ((rn = route_table_iter_next(&iter)) != NULL) {
+ if (rn->info) cb(rn->info, ctx);
+ }
+ route_table_iter_cleanup(&iter);
+}
+
+struct purge_ctx {
+ const struct prefix *p;
+ int deleted;
+};
+
+void nhrp_shortcut_purge(struct nhrp_shortcut *s, int force)
+{
+ THREAD_OFF(s->t_timer);
+ nhrp_reqid_free(&nhrp_packet_reqid, &s->reqid);
+
+ if (force) {
+ /* Immediate purge on route with draw or pending shortcut */
+ THREAD_TIMER_MSEC_ON(master, s->t_timer, nhrp_shortcut_do_purge, s, 5);
+ } else {
+ /* Soft expire - force immediate renewal, but purge
+ * in few seconds to make sure stale route is not
+ * used too long. In practice most purges are caused
+ * by hub bgp change, but target usually stays same.
+ * This allows to keep nhrp route up, and to not
+ * cause temporary rerouting via hubs causing latency
+ * jitter. */
+ THREAD_TIMER_MSEC_ON(master, s->t_timer, nhrp_shortcut_do_purge, s, 3000);
+ s->expiring = 1;
+ nhrp_shortcut_check_use(s);
+ }
+}
+
+static void nhrp_shortcut_purge_prefix(struct nhrp_shortcut *s, void *ctx)
+{
+ struct purge_ctx *pctx = ctx;
+
+ if (prefix_match(pctx->p, s->p))
+ nhrp_shortcut_purge(s, pctx->deleted || !s->cache);
+}
+
+void nhrp_shortcut_prefix_change(const struct prefix *p, int deleted)
+{
+ struct purge_ctx pctx = {
+ .p = p,
+ .deleted = deleted,
+ };
+ nhrp_shortcut_foreach(family2afi(PREFIX_FAMILY(p)), nhrp_shortcut_purge_prefix, &pctx);
+}
+
diff --git a/nhrpd/nhrp_vc.c b/nhrpd/nhrp_vc.c
new file mode 100644
index 0000000000..57fb462ab6
--- /dev/null
+++ b/nhrpd/nhrp_vc.c
@@ -0,0 +1,219 @@
+/* NHRP virtual connection
+ * Copyright (c) 2014-2015 Timo Teräs
+ *
+ * This file is free software: you may copy, redistribute 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.
+ */
+
+#include "zebra.h"
+#include "memory.h"
+#include "stream.h"
+#include "hash.h"
+#include "thread.h"
+#include "jhash.h"
+
+#include "nhrpd.h"
+#include "os.h"
+
+DEFINE_MTYPE_STATIC(NHRPD, NHRP_VC, "NHRP virtual connection")
+
+struct child_sa {
+ uint32_t id;
+ struct nhrp_vc *vc;
+ struct list_head childlist_entry;
+};
+
+static struct hash *nhrp_vc_hash;
+static struct list_head childlist_head[512];
+
+static unsigned int nhrp_vc_key(void *peer_data)
+{
+ struct nhrp_vc *vc = peer_data;
+ return jhash_2words(
+ sockunion_hash(&vc->local.nbma),
+ sockunion_hash(&vc->remote.nbma),
+ 0);
+}
+
+static int nhrp_vc_cmp(const void *cache_data, const void *key_data)
+{
+ const struct nhrp_vc *a = cache_data;
+ const struct nhrp_vc *b = key_data;
+ return sockunion_same(&a->local.nbma, &b->local.nbma) &&
+ sockunion_same(&a->remote.nbma, &b->remote.nbma);
+}
+
+static void *nhrp_vc_alloc(void *data)
+{
+ struct nhrp_vc *vc, *key = data;
+
+ vc = XMALLOC(MTYPE_NHRP_VC, sizeof(struct nhrp_vc));
+ if (vc) {
+ *vc = (struct nhrp_vc) {
+ .local.nbma = key->local.nbma,
+ .remote.nbma = key->remote.nbma,
+ .notifier_list = NOTIFIER_LIST_INITIALIZER(&vc->notifier_list),
+ };
+ }
+
+ return vc;
+}
+
+static void nhrp_vc_free(void *data)
+{
+ XFREE(MTYPE_NHRP_VC, data);
+}
+
+struct nhrp_vc *nhrp_vc_get(const union sockunion *src, const union sockunion *dst, int create)
+{
+ struct nhrp_vc key;
+ key.local.nbma = *src;
+ key.remote.nbma = *dst;
+ return hash_get(nhrp_vc_hash, &key, create ? nhrp_vc_alloc : 0);
+}
+
+static void nhrp_vc_check_delete(struct nhrp_vc *vc)
+{
+ if (vc->updating || vc->ipsec || notifier_active(&vc->notifier_list))
+ return;
+ hash_release(nhrp_vc_hash, vc);
+ nhrp_vc_free(vc);
+}
+
+static void nhrp_vc_update(struct nhrp_vc *vc, long cmd)
+{
+ vc->updating = 1;
+ notifier_call(&vc->notifier_list, cmd);
+ vc->updating = 0;
+ nhrp_vc_check_delete(vc);
+}
+
+static void nhrp_vc_ipsec_reset(struct nhrp_vc *vc)
+{
+ vc->local.id[0] = 0;
+ vc->local.certlen = 0;
+ vc->remote.id[0] = 0;
+ vc->remote.certlen = 0;
+}
+
+int nhrp_vc_ipsec_updown(uint32_t child_id, struct nhrp_vc *vc)
+{
+ char buf[2][SU_ADDRSTRLEN];
+ struct child_sa *sa = NULL, *lsa;
+ uint32_t child_hash = child_id % ZEBRA_NUM_OF(childlist_head);
+ int abort_migration = 0;
+
+ list_for_each_entry(lsa, &childlist_head[child_hash], childlist_entry) {
+ if (lsa->id == child_id) {
+ sa = lsa;
+ break;
+ }
+ }
+
+ if (!sa) {
+ if (!vc) return 0;
+
+ sa = XMALLOC(MTYPE_NHRP_VC, sizeof(struct child_sa));
+ if (!sa) return 0;
+
+ *sa = (struct child_sa) {
+ .id = child_id,
+ .childlist_entry = LIST_INITIALIZER(sa->childlist_entry),
+ .vc = NULL,
+ };
+ list_add_tail(&sa->childlist_entry, &childlist_head[child_hash]);
+ }
+
+ if (sa->vc == vc)
+ return 0;
+
+ if (vc) {
+ /* Attach first to new VC */
+ vc->ipsec++;
+ nhrp_vc_update(vc, NOTIFY_VC_IPSEC_CHANGED);
+ }
+ if (sa->vc && vc) {
+ /* Notify old VC of migration */
+ sa->vc->abort_migration = 0;
+ debugf(NHRP_DEBUG_COMMON, "IPsec NBMA change of %s to %s",
+ sockunion2str(&sa->vc->remote.nbma, buf[0], sizeof buf[0]),
+ sockunion2str(&vc->remote.nbma, buf[1], sizeof buf[1]));
+ nhrp_vc_update(sa->vc, NOTIFY_VC_IPSEC_UPDATE_NBMA);
+ abort_migration = sa->vc->abort_migration;
+ }
+ if (sa->vc) {
+ /* Deattach old VC */
+ sa->vc->ipsec--;
+ if (!sa->vc->ipsec) nhrp_vc_ipsec_reset(sa->vc);
+ nhrp_vc_update(sa->vc, NOTIFY_VC_IPSEC_CHANGED);
+ }
+
+ /* Update */
+ sa->vc = vc;
+ if (!vc) {
+ list_del(&sa->childlist_entry);
+ XFREE(MTYPE_NHRP_VC, sa);
+ }
+
+ return abort_migration;
+}
+
+void nhrp_vc_notify_add(struct nhrp_vc *vc, struct notifier_block *n, notifier_fn_t action)
+{
+ notifier_add(n, &vc->notifier_list, action);
+}
+
+void nhrp_vc_notify_del(struct nhrp_vc *vc, struct notifier_block *n)
+{
+ notifier_del(n);
+ nhrp_vc_check_delete(vc);
+}
+
+
+struct nhrp_vc_iterator_ctx {
+ void (*cb)(struct nhrp_vc *, void *);
+ void *ctx;
+};
+
+static void nhrp_vc_iterator(struct hash_backet *b, void *ctx)
+{
+ struct nhrp_vc_iterator_ctx *ic = ctx;
+ ic->cb(b->data, ic->ctx);
+}
+
+void nhrp_vc_foreach(void (*cb)(struct nhrp_vc *, void *), void *ctx)
+{
+ struct nhrp_vc_iterator_ctx ic = {
+ .cb = cb,
+ .ctx = ctx,
+ };
+ hash_iterate(nhrp_vc_hash, nhrp_vc_iterator, &ic);
+}
+
+void nhrp_vc_init(void)
+{
+ size_t i;
+
+ nhrp_vc_hash = hash_create(nhrp_vc_key, nhrp_vc_cmp);
+ for (i = 0; i < ZEBRA_NUM_OF(childlist_head); i++)
+ list_init(&childlist_head[i]);
+}
+
+void nhrp_vc_reset(void)
+{
+ struct child_sa *sa, *n;
+ size_t i;
+
+ for (i = 0; i < ZEBRA_NUM_OF(childlist_head); i++) {
+ list_for_each_entry_safe(sa, n, &childlist_head[i], childlist_entry)
+ nhrp_vc_ipsec_updown(sa->id, 0);
+ }
+}
+
+void nhrp_vc_terminate(void)
+{
+ nhrp_vc_reset();
+ hash_clean(nhrp_vc_hash, nhrp_vc_free);
+}
diff --git a/nhrpd/nhrp_vty.c b/nhrpd/nhrp_vty.c
new file mode 100644
index 0000000000..2e3164410c
--- /dev/null
+++ b/nhrpd/nhrp_vty.c
@@ -0,0 +1,924 @@
+/* NHRP vty handling
+ * Copyright (c) 2014-2015 Timo Teräs
+ *
+ * This file is free software: you may copy, redistribute 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.
+ */
+
+#include "zebra.h"
+#include "command.h"
+#include "zclient.h"
+#include "stream.h"
+
+#include "nhrpd.h"
+#include "netlink.h"
+
+static struct cmd_node zebra_node = {
+ .node = ZEBRA_NODE,
+ .prompt = "%s(config-router)# ",
+ .vtysh = 1,
+};
+
+static struct cmd_node nhrp_interface_node = {
+ .node = INTERFACE_NODE,
+ .prompt = "%s(config-if)# ",
+ .vtysh = 1,
+};
+
+#define NHRP_DEBUG_FLAGS_CMD "<all|common|event|interface|kernel|route|vici>"
+
+#define NHRP_DEBUG_FLAGS_STR \
+ "All messages\n" \
+ "Common messages (default)\n" \
+ "Event manager messages\n" \
+ "Interface messages\n" \
+ "Kernel messages\n" \
+ "Route messages\n" \
+ "VICI messages\n"
+
+static const struct message debug_flags_desc[] = {
+ { NHRP_DEBUG_ALL, "all" },
+ { NHRP_DEBUG_COMMON, "common" },
+ { NHRP_DEBUG_IF, "interface" },
+ { NHRP_DEBUG_KERNEL, "kernel" },
+ { NHRP_DEBUG_ROUTE, "route" },
+ { NHRP_DEBUG_VICI, "vici" },
+ { NHRP_DEBUG_EVENT, "event" },
+ { 0, NULL },
+};
+
+static const struct message interface_flags_desc[] = {
+ { NHRP_IFF_SHORTCUT, "shortcut" },
+ { NHRP_IFF_REDIRECT, "redirect" },
+ { NHRP_IFF_REG_NO_UNIQUE, "registration no-unique" },
+ { 0, NULL },
+};
+
+static int nhrp_vty_return(struct vty *vty, int ret)
+{
+ static const char * const errmsgs[] = {
+ [NHRP_ERR_FAIL] = "Command failed",
+ [NHRP_ERR_NO_MEMORY] = "Out of memory",
+ [NHRP_ERR_UNSUPPORTED_INTERFACE] = "NHRP not supported on this interface",
+ [NHRP_ERR_NHRP_NOT_ENABLED] = "NHRP not enabled (set 'nhrp network-id' first)",
+ [NHRP_ERR_ENTRY_EXISTS] = "Entry exists already",
+ [NHRP_ERR_ENTRY_NOT_FOUND] = "Entry not found",
+ [NHRP_ERR_PROTOCOL_ADDRESS_MISMATCH] = "Protocol address family does not match command (ip/ipv6 mismatch)",
+ };
+ const char *str = NULL;
+ char buf[256];
+
+ if (ret == NHRP_OK)
+ return CMD_SUCCESS;
+
+ if (ret > 0 && ret <= (int)ZEBRA_NUM_OF(errmsgs))
+ if (errmsgs[ret])
+ str = errmsgs[ret];
+
+ if (!str) {
+ str = buf;
+ snprintf(buf, sizeof(buf), "Unknown error %d", ret);
+ }
+
+ vty_out (vty, "%% %s%s", str, VTY_NEWLINE);
+
+ return CMD_WARNING;
+}
+
+static int toggle_flag(
+ struct vty *vty, const struct message *flag_desc,
+ const char *name, int on_off, unsigned *flags)
+{
+ int i;
+
+ for (i = 0; flag_desc[i].str != NULL; i++) {
+ if (strcmp(flag_desc[i].str, name) != 0)
+ continue;
+ if (on_off)
+ *flags |= flag_desc[i].key;
+ else
+ *flags &= ~flag_desc[i].key;
+ return CMD_SUCCESS;
+ }
+
+ vty_out(vty, "%% Invalid value %s%s", name, VTY_NEWLINE);
+ return CMD_WARNING;
+}
+
+#ifndef NO_DEBUG
+
+DEFUN(show_debugging_nhrp, show_debugging_nhrp_cmd,
+ "show debugging nhrp",
+ SHOW_STR
+ "Debugging information\n"
+ "NHRP configuration\n")
+{
+ int i;
+
+ vty_out(vty, "NHRP debugging status:%s", VTY_NEWLINE);
+
+ for (i = 0; debug_flags_desc[i].str != NULL; i++) {
+ if (debug_flags_desc[i].key == NHRP_DEBUG_ALL)
+ continue;
+ if (!(debug_flags_desc[i].key & debug_flags))
+ continue;
+
+ vty_out(vty, " NHRP %s debugging is on%s",
+ debug_flags_desc[i].str, VTY_NEWLINE);
+ }
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(debug_nhrp, debug_nhrp_cmd,
+ "debug nhrp " NHRP_DEBUG_FLAGS_CMD,
+ "Enable debug messages for specific or all parts.\n"
+ "NHRP information\n"
+ NHRP_DEBUG_FLAGS_STR)
+{
+ return toggle_flag(vty, debug_flags_desc, argv[2]->text, 1, &debug_flags);
+}
+
+DEFUN(no_debug_nhrp, no_debug_nhrp_cmd,
+ "no debug nhrp " NHRP_DEBUG_FLAGS_CMD,
+ NO_STR
+ "Disable debug messages for specific or all parts.\n"
+ "NHRP information\n"
+ NHRP_DEBUG_FLAGS_STR)
+{
+ return toggle_flag(vty, debug_flags_desc, argv[3]->text, 0, &debug_flags);
+}
+
+#endif /* NO_DEBUG */
+
+static int nhrp_config_write(struct vty *vty)
+{
+#ifndef NO_DEBUG
+ if (debug_flags == NHRP_DEBUG_ALL) {
+ vty_out(vty, "debug nhrp all%s", VTY_NEWLINE);
+ } else {
+ int i;
+
+ for (i = 0; debug_flags_desc[i].str != NULL; i++) {
+ if (debug_flags_desc[i].key == NHRP_DEBUG_ALL)
+ continue;
+ if (!(debug_flags & debug_flags_desc[i].key))
+ continue;
+ vty_out(vty, "debug nhrp %s%s", debug_flags_desc[i].str, VTY_NEWLINE);
+ }
+ }
+ vty_out(vty, "!%s", VTY_NEWLINE);
+#endif /* NO_DEBUG */
+
+ if (nhrp_event_socket_path) {
+ vty_out(vty, "nhrp event socket %s%s",
+ nhrp_event_socket_path, VTY_NEWLINE);
+ }
+ if (netlink_nflog_group) {
+ vty_out(vty, "nhrp nflog-group %d%s",
+ netlink_nflog_group, VTY_NEWLINE);
+ }
+
+ return 0;
+}
+
+#define IP_STR "IP information\n"
+#define IPV6_STR "IPv6 information\n"
+#define AFI_CMD "<ip|ipv6>"
+#define AFI_STR IP_STR IPV6_STR
+#define NHRP_STR "Next Hop Resolution Protocol functions\n"
+
+static afi_t cmd_to_afi(const struct cmd_token *tok)
+{
+ return strcmp(tok->text, "ipv6") == 0 ? AFI_IP6 : AFI_IP;
+}
+
+static const char *afi_to_cmd(afi_t afi)
+{
+ if (afi == AFI_IP6) return "ipv6";
+ return "ip";
+}
+
+DEFUN(nhrp_event_socket, nhrp_event_socket_cmd,
+ "nhrp event socket SOCKET",
+ NHRP_STR
+ "Event Manager commands\n"
+ "Event Manager unix socket path\n"
+ "Unix path for the socket")
+{
+ evmgr_set_socket(argv[3]->arg);
+ return CMD_SUCCESS;
+}
+
+DEFUN(no_nhrp_event_socket, no_nhrp_event_socket_cmd,
+ "no nhrp event socket [SOCKET]",
+ NO_STR
+ NHRP_STR
+ "Event Manager commands\n"
+ "Event Manager unix socket path\n"
+ "Unix path for the socket")
+{
+ evmgr_set_socket(NULL);
+ return CMD_SUCCESS;
+}
+
+DEFUN(nhrp_nflog_group, nhrp_nflog_group_cmd,
+ "nhrp nflog-group (1-65535)",
+ NHRP_STR
+ "Specify NFLOG group number\n"
+ "NFLOG group number\n")
+{
+ uint32_t nfgroup;
+
+ VTY_GET_INTEGER_RANGE("nflog-group", nfgroup, argv[2]->arg, 1, 65535);
+ netlink_set_nflog_group(nfgroup);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(no_nhrp_nflog_group, no_nhrp_nflog_group_cmd,
+ "no nhrp nflog-group [(1-65535)]",
+ NO_STR
+ NHRP_STR
+ "Specify NFLOG group number\n"
+ "NFLOG group number\n")
+{
+ netlink_set_nflog_group(0);
+ return CMD_SUCCESS;
+}
+
+DEFUN(tunnel_protection, tunnel_protection_cmd,
+ "tunnel protection vici profile PROFILE [fallback-profile FALLBACK]",
+ "NHRP/GRE integration\n"
+ "IPsec protection\n"
+ "VICI (StrongSwan)\n"
+ "IPsec profile\n"
+ "IPsec profile name\n"
+ "Fallback IPsec profile\n"
+ "Fallback IPsec profile name\n")
+{
+ VTY_DECLVAR_CONTEXT(interface, ifp);
+
+ nhrp_interface_set_protection(ifp, argv[4]->arg,
+ argc > 6 ? argv[6]->arg : NULL);
+ return CMD_SUCCESS;
+}
+
+DEFUN(no_tunnel_protection, no_tunnel_protection_cmd,
+ "no tunnel protection",
+ NO_STR
+ "NHRP/GRE integration\n"
+ "IPsec protection\n")
+{
+ VTY_DECLVAR_CONTEXT(interface, ifp);
+
+ nhrp_interface_set_protection(ifp, NULL, NULL);
+ return CMD_SUCCESS;
+}
+
+DEFUN(tunnel_source, tunnel_source_cmd,
+ "tunnel source INTERFACE",
+ "NHRP/GRE integration\n"
+ "Tunnel device binding tracking\n"
+ "Interface name\n")
+{
+ VTY_DECLVAR_CONTEXT(interface, ifp);
+ nhrp_interface_set_source(ifp, argv[2]->arg);
+ return CMD_SUCCESS;
+}
+
+DEFUN(no_tunnel_source, no_tunnel_source_cmd,
+ "no tunnel source",
+ "NHRP/GRE integration\n"
+ "Tunnel device binding tracking\n"
+ "Interface name\n")
+{
+ VTY_DECLVAR_CONTEXT(interface, ifp);
+ nhrp_interface_set_source(ifp, NULL);
+ return CMD_SUCCESS;
+}
+
+DEFUN(if_nhrp_network_id, if_nhrp_network_id_cmd,
+ AFI_CMD " nhrp network-id (1-4294967295)",
+ AFI_STR
+ NHRP_STR
+ "Enable NHRP and specify network-id\n"
+ "System local ID to specify interface group\n")
+{
+ VTY_DECLVAR_CONTEXT(interface, ifp);
+ struct nhrp_interface *nifp = ifp->info;
+ afi_t afi = cmd_to_afi(argv[0]);
+
+ VTY_GET_INTEGER_RANGE("network-id", nifp->afi[afi].network_id, argv[3]->arg, 1, 4294967295);
+ nhrp_interface_update(ifp);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(if_no_nhrp_network_id, if_no_nhrp_network_id_cmd,
+ "no " AFI_CMD " nhrp network-id [(1-4294967295)]",
+ NO_STR
+ AFI_STR
+ NHRP_STR
+ "Enable NHRP and specify network-id\n"
+ "System local ID to specify interface group\n")
+{
+ VTY_DECLVAR_CONTEXT(interface, ifp);
+ struct nhrp_interface *nifp = ifp->info;
+ afi_t afi = cmd_to_afi(argv[1]);
+
+ nifp->afi[afi].network_id = 0;
+ nhrp_interface_update(ifp);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(if_nhrp_flags, if_nhrp_flags_cmd,
+ AFI_CMD " nhrp <shortcut|redirect>",
+ AFI_STR
+ NHRP_STR
+ "Allow shortcut establishment\n"
+ "Send redirect notifications\n")
+{
+ VTY_DECLVAR_CONTEXT(interface, ifp);
+ struct nhrp_interface *nifp = ifp->info;
+ afi_t afi = cmd_to_afi(argv[0]);
+
+ return toggle_flag(vty, interface_flags_desc, argv[2]->text, 1, &nifp->afi[afi].flags);
+}
+
+DEFUN(if_no_nhrp_flags, if_no_nhrp_flags_cmd,
+ "no " AFI_CMD " nhrp <shortcut|redirect>",
+ NO_STR
+ AFI_STR
+ NHRP_STR
+ "Allow shortcut establishment\n"
+ "Send redirect notifications\n")
+{
+ VTY_DECLVAR_CONTEXT(interface, ifp);
+ struct nhrp_interface *nifp = ifp->info;
+ afi_t afi = cmd_to_afi(argv[1]);
+
+ return toggle_flag(vty, interface_flags_desc, argv[3]->text, 0, &nifp->afi[afi].flags);
+}
+
+DEFUN(if_nhrp_reg_flags, if_nhrp_reg_flags_cmd,
+ AFI_CMD " nhrp registration no-unique",
+ AFI_STR
+ NHRP_STR
+ "Registration configuration\n"
+ "Don't set unique flag\n")
+{
+ VTY_DECLVAR_CONTEXT(interface, ifp);
+ struct nhrp_interface *nifp = ifp->info;
+ afi_t afi = cmd_to_afi(argv[0]);
+ char name[256];
+ snprintf(name, sizeof(name), "registration %s", argv[3]->text);
+ return toggle_flag(vty, interface_flags_desc, name, 1, &nifp->afi[afi].flags);
+}
+
+DEFUN(if_no_nhrp_reg_flags, if_no_nhrp_reg_flags_cmd,
+ "no " AFI_CMD " nhrp registration no-unique",
+ NO_STR
+ AFI_STR
+ NHRP_STR
+ "Registration configuration\n"
+ "Don't set unique flag\n")
+{
+ VTY_DECLVAR_CONTEXT(interface, ifp);
+ struct nhrp_interface *nifp = ifp->info;
+ afi_t afi = cmd_to_afi(argv[1]);
+ char name[256];
+ snprintf(name, sizeof(name), "registration %s", argv[4]->text);
+ return toggle_flag(vty, interface_flags_desc, name, 0, &nifp->afi[afi].flags);
+}
+
+DEFUN(if_nhrp_holdtime, if_nhrp_holdtime_cmd,
+ AFI_CMD " nhrp holdtime (1-65000)",
+ AFI_STR
+ NHRP_STR
+ "Specify NBMA address validity time\n"
+ "Time in seconds that NBMA addresses are advertised valid\n")
+{
+ VTY_DECLVAR_CONTEXT(interface, ifp);
+ struct nhrp_interface *nifp = ifp->info;
+ afi_t afi = cmd_to_afi(argv[0]);
+
+ VTY_GET_INTEGER_RANGE("holdtime", nifp->afi[afi].holdtime, argv[3]->arg, 1, 65000);
+ nhrp_interface_update(ifp);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(if_no_nhrp_holdtime, if_no_nhrp_holdtime_cmd,
+ "no " AFI_CMD " nhrp holdtime [(1-65000)]",
+ NO_STR
+ AFI_STR
+ NHRP_STR
+ "Specify NBMA address validity time\n"
+ "Time in seconds that NBMA addresses are advertised valid\n")
+{
+ VTY_DECLVAR_CONTEXT(interface, ifp);
+ struct nhrp_interface *nifp = ifp->info;
+ afi_t afi = cmd_to_afi(argv[1]);
+
+ nifp->afi[afi].holdtime = NHRPD_DEFAULT_HOLDTIME;
+ nhrp_interface_update(ifp);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(if_nhrp_mtu, if_nhrp_mtu_cmd,
+ "ip nhrp mtu <(576-1500)|opennhrp>",
+ IP_STR
+ NHRP_STR
+ "Configure NHRP advertised MTU\n"
+ "MTU value\n"
+ "Advertise bound interface MTU similar to OpenNHRP")
+{
+ VTY_DECLVAR_CONTEXT(interface, ifp);
+ struct nhrp_interface *nifp = ifp->info;
+
+ if (argv[3]->arg[0] == 'o') {
+ nifp->afi[AFI_IP].configured_mtu = -1;
+ } else {
+ VTY_GET_INTEGER_RANGE("mtu", nifp->afi[AFI_IP].configured_mtu, argv[3]->arg, 576, 1500);
+ }
+ nhrp_interface_update_mtu(ifp, AFI_IP);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(if_no_nhrp_mtu, if_no_nhrp_mtu_cmd,
+ "no ip nhrp mtu [(576-1500)|opennhrp]",
+ NO_STR
+ IP_STR
+ NHRP_STR
+ "Configure NHRP advertised MTU\n"
+ "MTU value\n"
+ "Advertise bound interface MTU similar to OpenNHRP")
+{
+ VTY_DECLVAR_CONTEXT(interface, ifp);
+ struct nhrp_interface *nifp = ifp->info;
+
+ nifp->afi[AFI_IP].configured_mtu = 0;
+ nhrp_interface_update_mtu(ifp, AFI_IP);
+ return CMD_SUCCESS;
+}
+
+DEFUN(if_nhrp_map, if_nhrp_map_cmd,
+ AFI_CMD " nhrp map <A.B.C.D|X:X::X:X> <A.B.C.D|local>",
+ AFI_STR
+ NHRP_STR
+ "Nexthop Server configuration\n"
+ "IPv4 protocol address\n"
+ "IPv6 protocol address\n"
+ "IPv4 NBMA address\n"
+ "Handle protocol address locally\n")
+{
+ VTY_DECLVAR_CONTEXT(interface, ifp);
+ afi_t afi = cmd_to_afi(argv[0]);
+ union sockunion proto_addr, nbma_addr;
+ struct nhrp_cache *c;
+
+ if (str2sockunion(argv[3]->arg, &proto_addr) < 0 ||
+ afi2family(afi) != sockunion_family(&proto_addr))
+ return nhrp_vty_return(vty, NHRP_ERR_PROTOCOL_ADDRESS_MISMATCH);
+
+ c = nhrp_cache_get(ifp, &proto_addr, 1);
+ if (!c)
+ return nhrp_vty_return(vty, NHRP_ERR_FAIL);
+
+ c->map = 1;
+ if (strcmp(argv[4]->text, "local") == 0) {
+ nhrp_cache_update_binding(c, NHRP_CACHE_LOCAL, 0, NULL, 0, NULL);
+ } else{
+ if (str2sockunion(argv[4]->arg, &nbma_addr) < 0)
+ return nhrp_vty_return(vty, NHRP_ERR_FAIL);
+ nhrp_cache_update_binding(c, NHRP_CACHE_STATIC, 0,
+ nhrp_peer_get(ifp, &nbma_addr), 0, NULL);
+ }
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(if_nhrp_nhs, if_nhrp_nhs_cmd,
+ AFI_CMD " nhrp nhs <A.B.C.D|X:X::X:X|dynamic> nbma <A.B.C.D|FQDN>",
+ AFI_STR
+ NHRP_STR
+ "Nexthop Server configuration\n"
+ "IPv4 protocol address\n"
+ "IPv6 protocol address\n"
+ "Automatic detection of protocol address\n"
+ "NBMA address\n"
+ "IPv4 NBMA address\n"
+ "Fully qualified domain name for NBMA address(es)\n")
+{
+ VTY_DECLVAR_CONTEXT(interface, ifp);
+ afi_t afi = cmd_to_afi(argv[0]);
+ union sockunion proto_addr;
+ int ret;
+
+ if (str2sockunion(argv[3]->arg, &proto_addr) < 0)
+ sockunion_family(&proto_addr) = AF_UNSPEC;
+
+ ret = nhrp_nhs_add(ifp, afi, &proto_addr, argv[5]->arg);
+ return nhrp_vty_return(vty, ret);
+}
+
+DEFUN(if_no_nhrp_nhs, if_no_nhrp_nhs_cmd,
+ "no " AFI_CMD " nhrp nhs <A.B.C.D|X:X::X:X|dynamic> nbma <A.B.C.D|FQDN>",
+ NO_STR
+ AFI_STR
+ NHRP_STR
+ "Nexthop Server configuration\n"
+ "IPv4 protocol address\n"
+ "IPv6 protocol address\n"
+ "Automatic detection of protocol address\n"
+ "NBMA address\n"
+ "IPv4 NBMA address\n"
+ "Fully qualified domain name for NBMA address(es)\n")
+{
+ VTY_DECLVAR_CONTEXT(interface, ifp);
+ afi_t afi = cmd_to_afi(argv[1]);
+ union sockunion proto_addr;
+ int ret;
+
+ if (str2sockunion(argv[4]->arg, &proto_addr) < 0)
+ sockunion_family(&proto_addr) = AF_UNSPEC;
+
+ ret = nhrp_nhs_del(ifp, afi, &proto_addr, argv[6]->arg);
+ return nhrp_vty_return(vty, ret);
+}
+
+struct info_ctx {
+ struct vty *vty;
+ afi_t afi;
+ int count;
+};
+
+static void show_ip_nhrp_cache(struct nhrp_cache *c, void *pctx)
+{
+ struct info_ctx *ctx = pctx;
+ struct vty *vty = ctx->vty;
+ char buf[2][SU_ADDRSTRLEN];
+
+ if (ctx->afi != family2afi(sockunion_family(&c->remote_addr)))
+ return;
+
+ if (!ctx->count) {
+ vty_out(vty, "%-8s %-8s %-24s %-24s %-6s %s%s",
+ "Iface",
+ "Type",
+ "Protocol",
+ "NBMA",
+ "Flags",
+ "Identity",
+ VTY_NEWLINE);
+ }
+ ctx->count++;
+
+ vty_out(ctx->vty, "%-8s %-8s %-24s %-24s %c%c%c %s%s",
+ c->ifp->name,
+ nhrp_cache_type_str[c->cur.type],
+ sockunion2str(&c->remote_addr, buf[0], sizeof buf[0]),
+ c->cur.peer ? sockunion2str(&c->cur.peer->vc->remote.nbma, buf[1], sizeof buf[1]) : "-",
+ c->used ? 'U' : ' ',
+ c->t_timeout ? 'T' : ' ',
+ c->t_auth ? 'A' : ' ',
+ c->cur.peer ? c->cur.peer->vc->remote.id : "-",
+ VTY_NEWLINE);
+}
+
+static void show_ip_opennhrp_cache(struct nhrp_cache *c, void *pctx)
+{
+ struct info_ctx *ctx = pctx;
+ struct vty *vty = ctx->vty;
+ char buf[SU_ADDRSTRLEN];
+
+ if (ctx->afi != family2afi(sockunion_family(&c->remote_addr)))
+ return;
+
+ vty_out(ctx->vty,
+ "Type: %s%s"
+ "Flags:%s%s%s"
+ "Protocol-Address: %s/%zu%s",
+ nhrp_cache_type_str[c->cur.type],
+ VTY_NEWLINE,
+ (c->cur.peer && c->cur.peer->online) ? " up": "",
+ c->used ? " used": "",
+ VTY_NEWLINE,
+ sockunion2str(&c->remote_addr, buf, sizeof buf),
+ 8 * family2addrsize(sockunion_family(&c->remote_addr)),
+ VTY_NEWLINE);
+
+ if (c->cur.peer) {
+ vty_out(ctx->vty,
+ "NBMA-Address: %s%s",
+ sockunion2str(&c->cur.peer->vc->remote.nbma, buf, sizeof buf),
+ VTY_NEWLINE);
+ }
+
+ if (sockunion_family(&c->cur.remote_nbma_natoa) != AF_UNSPEC) {
+ vty_out(ctx->vty,
+ "NBMA-NAT-OA-Address: %s%s",
+ sockunion2str(&c->cur.remote_nbma_natoa, buf, sizeof buf),
+ VTY_NEWLINE);
+ }
+
+ vty_out(ctx->vty, "%s", VTY_NEWLINE);
+}
+
+static void show_ip_nhrp_shortcut(struct nhrp_shortcut *s, void *pctx)
+{
+ struct info_ctx *ctx = pctx;
+ struct nhrp_cache *c;
+ struct vty *vty = ctx->vty;
+ char buf1[PREFIX_STRLEN], buf2[SU_ADDRSTRLEN];
+
+ if (!ctx->count) {
+ vty_out(vty, "%-8s %-24s %-24s %s%s",
+ "Type",
+ "Prefix",
+ "Via",
+ "Identity",
+ VTY_NEWLINE);
+ }
+ ctx->count++;
+
+ c = s->cache;
+ vty_out(ctx->vty, "%-8s %-24s %-24s %s%s",
+ nhrp_cache_type_str[s->type],
+ prefix2str(s->p, buf1, sizeof buf1),
+ c ? sockunion2str(&c->remote_addr, buf2, sizeof buf2) : "",
+ (c && c->cur.peer) ? c->cur.peer->vc->remote.id : "",
+ VTY_NEWLINE);
+}
+
+DEFUN(show_ip_nhrp, show_ip_nhrp_cmd,
+ "show " AFI_CMD " nhrp [cache|shortcut|opennhrp]",
+ SHOW_STR
+ AFI_STR
+ "NHRP information\n"
+ "Forwarding cache information\n"
+ "Shortcut information\n"
+ "opennhrpctl style cache dump\n")
+{
+ struct listnode *node;
+ struct interface *ifp;
+ struct info_ctx ctx = {
+ .vty = vty,
+ .afi = cmd_to_afi(argv[1]),
+ };
+
+ if (argc <= 3 || argv[3]->text[0] == 'c') {
+ for (ALL_LIST_ELEMENTS_RO(vrf_iflist(VRF_DEFAULT), node, ifp))
+ nhrp_cache_foreach(ifp, show_ip_nhrp_cache, &ctx);
+ } else if (argv[3]->text[0] == 'o') {
+ vty_out(vty, "Status: ok%s%s", VTY_NEWLINE, VTY_NEWLINE);
+ ctx.count++;
+ for (ALL_LIST_ELEMENTS_RO(vrf_iflist(VRF_DEFAULT), node, ifp))
+ nhrp_cache_foreach(ifp, show_ip_opennhrp_cache, &ctx);
+ } else {
+ nhrp_shortcut_foreach(ctx.afi, show_ip_nhrp_shortcut, &ctx);
+ }
+
+ if (!ctx.count) {
+ vty_out(vty, "%% No entries%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ return CMD_SUCCESS;
+}
+
+static void show_dmvpn_entry(struct nhrp_vc *vc, void *ctx)
+{
+ struct vty *vty = ctx;
+ char buf[2][SU_ADDRSTRLEN];
+
+ vty_out(vty, "%-24s %-24s %c %-4d %-24s%s",
+ sockunion2str(&vc->local.nbma, buf[0], sizeof buf[0]),
+ sockunion2str(&vc->remote.nbma, buf[1], sizeof buf[1]),
+ notifier_active(&vc->notifier_list) ? 'n' : ' ',
+ vc->ipsec,
+ vc->remote.id,
+ VTY_NEWLINE);
+}
+
+DEFUN(show_dmvpn, show_dmvpn_cmd,
+ "show dmvpn",
+ SHOW_STR
+ "DMVPN information\n")
+{
+ vty_out(vty, "%-24s %-24s %-6s %-4s %-24s%s",
+ "Src",
+ "Dst",
+ "Flags",
+ "SAs",
+ "Identity",
+ VTY_NEWLINE);
+
+ nhrp_vc_foreach(show_dmvpn_entry, vty);
+
+ return CMD_SUCCESS;
+}
+
+static void clear_nhrp_cache(struct nhrp_cache *c, void *data)
+{
+ struct info_ctx *ctx = data;
+ if (c->cur.type <= NHRP_CACHE_CACHED) {
+ nhrp_cache_update_binding(c, c->cur.type, -1, NULL, 0, NULL);
+ ctx->count++;
+ }
+}
+
+static void clear_nhrp_shortcut(struct nhrp_shortcut *s, void *data)
+{
+ struct info_ctx *ctx = data;
+ nhrp_shortcut_purge(s, 1);
+ ctx->count++;
+}
+
+DEFUN(clear_nhrp, clear_nhrp_cmd,
+ "clear " AFI_CMD " nhrp <cache|shortcut>",
+ CLEAR_STR
+ AFI_STR
+ NHRP_STR
+ "Dynamic cache entries\n"
+ "Shortcut entries\n")
+{
+ struct listnode *node;
+ struct interface *ifp;
+ struct info_ctx ctx = {
+ .vty = vty,
+ .afi = cmd_to_afi(argv[1]),
+ .count = 0,
+ };
+
+ if (argc <= 3 || argv[3]->text[0] == 'c') {
+ for (ALL_LIST_ELEMENTS_RO(vrf_iflist(VRF_DEFAULT), node, ifp))
+ nhrp_cache_foreach(ifp, clear_nhrp_cache, &ctx);
+ } else {
+ nhrp_shortcut_foreach(ctx.afi, clear_nhrp_shortcut, &ctx);
+ }
+
+ if (!ctx.count) {
+ vty_out(vty, "%% No entries%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ vty_out(vty, "%% %d entries cleared%s", ctx.count, VTY_NEWLINE);
+ return CMD_SUCCESS;
+}
+
+struct write_map_ctx {
+ struct vty *vty;
+ int family;
+ const char *aficmd;
+};
+
+static void interface_config_write_nhrp_map(struct nhrp_cache *c, void *data)
+{
+ struct write_map_ctx *ctx = data;
+ struct vty *vty = ctx->vty;
+ char buf[2][SU_ADDRSTRLEN];
+
+ if (!c->map) return;
+ if (sockunion_family(&c->remote_addr) != ctx->family) return;
+
+ vty_out(vty, " %s nhrp map %s %s%s",
+ ctx->aficmd,
+ sockunion2str(&c->remote_addr, buf[0], sizeof buf[0]),
+ c->cur.type == NHRP_CACHE_LOCAL ? "local" :
+ sockunion2str(&c->cur.peer->vc->remote.nbma, buf[1], sizeof buf[1]),
+ VTY_NEWLINE);
+}
+
+static int interface_config_write(struct vty *vty)
+{
+ struct write_map_ctx mapctx;
+ struct listnode *node;
+ struct interface *ifp;
+ struct nhrp_interface *nifp;
+ struct nhrp_nhs *nhs;
+ const char *aficmd;
+ afi_t afi;
+ char buf[SU_ADDRSTRLEN];
+ int i;
+
+ for (ALL_LIST_ELEMENTS_RO(vrf_iflist(VRF_DEFAULT), node, ifp)) {
+ vty_out(vty, "interface %s%s", ifp->name, VTY_NEWLINE);
+ if (ifp->desc)
+ vty_out(vty, " description %s%s", ifp->desc, VTY_NEWLINE);
+
+ nifp = ifp->info;
+ if (nifp->ipsec_profile) {
+ vty_out(vty, " tunnel protection vici profile %s",
+ nifp->ipsec_profile);
+ if (nifp->ipsec_fallback_profile)
+ vty_out(vty, " fallback-profile %s",
+ nifp->ipsec_fallback_profile);
+ vty_out(vty, "%s", VTY_NEWLINE);
+ }
+ if (nifp->source)
+ vty_out(vty, " tunnel source %s%s",
+ nifp->source, VTY_NEWLINE);
+
+ for (afi = 0; afi < AFI_MAX; afi++) {
+ struct nhrp_afi_data *ad = &nifp->afi[afi];
+
+ aficmd = afi_to_cmd(afi);
+
+ if (ad->network_id)
+ vty_out(vty, " %s nhrp network-id %u%s",
+ aficmd, ad->network_id,
+ VTY_NEWLINE);
+
+ if (ad->holdtime != NHRPD_DEFAULT_HOLDTIME)
+ vty_out(vty, " %s nhrp holdtime %u%s",
+ aficmd, ad->holdtime,
+ VTY_NEWLINE);
+
+ if (ad->configured_mtu < 0)
+ vty_out(vty, " %s nhrp mtu opennhrp%s",
+ aficmd, VTY_NEWLINE);
+ else if (ad->configured_mtu)
+ vty_out(vty, " %s nhrp mtu %u%s",
+ aficmd, ad->configured_mtu,
+ VTY_NEWLINE);
+
+ for (i = 0; interface_flags_desc[i].str != NULL; i++) {
+ if (!(ad->flags & interface_flags_desc[i].key))
+ continue;
+ vty_out(vty, " %s nhrp %s%s",
+ aficmd, interface_flags_desc[i].str, VTY_NEWLINE);
+ }
+
+ mapctx = (struct write_map_ctx) {
+ .vty = vty,
+ .family = afi2family(afi),
+ .aficmd = aficmd,
+ };
+ nhrp_cache_foreach(ifp, interface_config_write_nhrp_map, &mapctx);
+
+ list_for_each_entry(nhs, &ad->nhslist_head, nhslist_entry) {
+ vty_out(vty, " %s nhrp nhs %s nbma %s%s",
+ aficmd,
+ sockunion_family(&nhs->proto_addr) == AF_UNSPEC ? "dynamic" : sockunion2str(&nhs->proto_addr, buf, sizeof buf),
+ nhs->nbma_fqdn,
+ VTY_NEWLINE);
+ }
+ }
+
+ vty_out (vty, "!%s", VTY_NEWLINE);
+ }
+
+ return 0;
+}
+
+void nhrp_config_init(void)
+{
+ install_node(&zebra_node, nhrp_config_write);
+ install_default(ZEBRA_NODE);
+
+ /* global commands */
+ install_element(VIEW_NODE, &show_debugging_nhrp_cmd);
+ install_element(VIEW_NODE, &show_ip_nhrp_cmd);
+ install_element(VIEW_NODE, &show_dmvpn_cmd);
+ install_element(ENABLE_NODE, &clear_nhrp_cmd);
+
+ install_element(ENABLE_NODE, &debug_nhrp_cmd);
+ install_element(ENABLE_NODE, &no_debug_nhrp_cmd);
+
+ install_element(CONFIG_NODE, &debug_nhrp_cmd);
+ install_element(CONFIG_NODE, &no_debug_nhrp_cmd);
+
+ install_element(CONFIG_NODE, &nhrp_event_socket_cmd);
+ install_element(CONFIG_NODE, &no_nhrp_event_socket_cmd);
+ install_element(CONFIG_NODE, &nhrp_nflog_group_cmd);
+ install_element(CONFIG_NODE, &no_nhrp_nflog_group_cmd);
+
+ /* interface specific commands */
+ install_node(&nhrp_interface_node, interface_config_write);
+
+ if_cmd_init();
+ install_element(INTERFACE_NODE, &tunnel_protection_cmd);
+ install_element(INTERFACE_NODE, &no_tunnel_protection_cmd);
+ install_element(INTERFACE_NODE, &tunnel_source_cmd);
+ install_element(INTERFACE_NODE, &no_tunnel_source_cmd);
+ install_element(INTERFACE_NODE, &if_nhrp_network_id_cmd);
+ install_element(INTERFACE_NODE, &if_no_nhrp_network_id_cmd);
+ install_element(INTERFACE_NODE, &if_nhrp_holdtime_cmd);
+ install_element(INTERFACE_NODE, &if_no_nhrp_holdtime_cmd);
+ install_element(INTERFACE_NODE, &if_nhrp_mtu_cmd);
+ install_element(INTERFACE_NODE, &if_no_nhrp_mtu_cmd);
+ install_element(INTERFACE_NODE, &if_nhrp_flags_cmd);
+ install_element(INTERFACE_NODE, &if_no_nhrp_flags_cmd);
+ install_element(INTERFACE_NODE, &if_nhrp_reg_flags_cmd);
+ install_element(INTERFACE_NODE, &if_no_nhrp_reg_flags_cmd);
+ install_element(INTERFACE_NODE, &if_nhrp_map_cmd);
+ install_element(INTERFACE_NODE, &if_nhrp_nhs_cmd);
+ install_element(INTERFACE_NODE, &if_no_nhrp_nhs_cmd);
+}
diff --git a/nhrpd/nhrpd.h b/nhrpd/nhrpd.h
new file mode 100644
index 0000000000..cd2b0d5580
--- /dev/null
+++ b/nhrpd/nhrpd.h
@@ -0,0 +1,403 @@
+/* NHRP daemon internal structures and function prototypes
+ * Copyright (c) 2014-2015 Timo Teräs
+ *
+ * This file is free software: you may copy, redistribute 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.
+ */
+
+#ifndef NHRPD_H
+#define NHRPD_H
+
+#include "list.h"
+
+#include "zbuf.h"
+#include "zclient.h"
+#include "debug.h"
+#include "memory.h"
+
+DECLARE_MGROUP(NHRPD)
+
+#define NHRPD_DEFAULT_HOLDTIME 7200
+
+#define NHRP_VTY_PORT 2610
+#define NHRP_DEFAULT_CONFIG "nhrpd.conf"
+
+extern struct thread_master *master;
+
+enum {
+ NHRP_OK = 0,
+ NHRP_ERR_FAIL,
+ NHRP_ERR_NO_MEMORY,
+ NHRP_ERR_UNSUPPORTED_INTERFACE,
+ NHRP_ERR_NHRP_NOT_ENABLED,
+ NHRP_ERR_ENTRY_EXISTS,
+ NHRP_ERR_ENTRY_NOT_FOUND,
+ NHRP_ERR_PROTOCOL_ADDRESS_MISMATCH,
+};
+
+struct notifier_block;
+
+typedef void (*notifier_fn_t)(struct notifier_block *, unsigned long);
+
+struct notifier_block {
+ struct list_head notifier_entry;
+ notifier_fn_t action;
+};
+
+struct notifier_list {
+ struct list_head notifier_head;
+};
+
+#define NOTIFIER_LIST_INITIALIZER(l) \
+ { .notifier_head = LIST_INITIALIZER((l)->notifier_head) }
+
+static inline void notifier_init(struct notifier_list *l)
+{
+ list_init(&l->notifier_head);
+}
+
+static inline void notifier_add(struct notifier_block *n, struct notifier_list *l, notifier_fn_t action)
+{
+ n->action = action;
+ list_add_tail(&n->notifier_entry, &l->notifier_head);
+}
+
+static inline void notifier_del(struct notifier_block *n)
+{
+ list_del(&n->notifier_entry);
+}
+
+static inline void notifier_call(struct notifier_list *l, int cmd)
+{
+ struct notifier_block *n, *nn;
+ list_for_each_entry_safe(n, nn, &l->notifier_head, notifier_entry)
+ n->action(n, cmd);
+}
+
+static inline int notifier_active(struct notifier_list *l)
+{
+ return !list_empty(&l->notifier_head);
+}
+
+struct resolver_query {
+ void (*callback)(struct resolver_query *, int n, union sockunion *);
+};
+
+void resolver_init(void);
+void resolver_resolve(struct resolver_query *query, int af, const char *hostname, void (*cb)(struct resolver_query *, int, union sockunion *));
+
+void nhrp_zebra_init(void);
+void nhrp_zebra_terminate(void);
+
+struct zbuf;
+struct nhrp_vc;
+struct nhrp_cache;
+struct nhrp_nhs;
+struct nhrp_interface;
+
+#define MAX_ID_LENGTH 64
+#define MAX_CERT_LENGTH 2048
+
+enum nhrp_notify_type {
+ NOTIFY_INTERFACE_UP,
+ NOTIFY_INTERFACE_DOWN,
+ NOTIFY_INTERFACE_CHANGED,
+ NOTIFY_INTERFACE_ADDRESS_CHANGED,
+ NOTIFY_INTERFACE_NBMA_CHANGED,
+ NOTIFY_INTERFACE_MTU_CHANGED,
+
+ NOTIFY_VC_IPSEC_CHANGED,
+ NOTIFY_VC_IPSEC_UPDATE_NBMA,
+
+ NOTIFY_PEER_UP,
+ NOTIFY_PEER_DOWN,
+ NOTIFY_PEER_IFCONFIG_CHANGED,
+ NOTIFY_PEER_MTU_CHANGED,
+ NOTIFY_PEER_NBMA_CHANGING,
+
+ NOTIFY_CACHE_UP,
+ NOTIFY_CACHE_DOWN,
+ NOTIFY_CACHE_DELETE,
+ NOTIFY_CACHE_USED,
+ NOTIFY_CACHE_BINDING_CHANGE,
+};
+
+struct nhrp_vc {
+ struct notifier_list notifier_list;
+ uint8_t ipsec;
+ uint8_t updating;
+ uint8_t abort_migration;
+
+ struct nhrp_vc_peer {
+ union sockunion nbma;
+ char id[MAX_ID_LENGTH];
+ uint16_t certlen;
+ uint8_t cert[MAX_CERT_LENGTH];
+ } local, remote;
+};
+
+enum nhrp_route_type {
+ NHRP_ROUTE_BLACKHOLE,
+ NHRP_ROUTE_LOCAL,
+ NHRP_ROUTE_NBMA_NEXTHOP,
+ NHRP_ROUTE_OFF_NBMA,
+};
+
+struct nhrp_peer {
+ unsigned int ref;
+ unsigned online : 1;
+ unsigned requested : 1;
+ unsigned fallback_requested : 1;
+ unsigned prio : 1;
+ struct notifier_list notifier_list;
+ struct interface *ifp;
+ struct nhrp_vc *vc;
+ struct thread *t_fallback;
+ struct notifier_block vc_notifier, ifp_notifier;
+};
+
+struct nhrp_packet_parser {
+ struct interface *ifp;
+ struct nhrp_afi_data *if_ad;
+ struct nhrp_peer *peer;
+ struct zbuf *pkt;
+ struct zbuf payload;
+ struct zbuf extensions;
+ struct nhrp_packet_header *hdr;
+ enum nhrp_route_type route_type;
+ struct prefix route_prefix;
+ union sockunion src_nbma, src_proto, dst_proto;
+};
+
+struct nhrp_reqid_pool {
+ struct hash *reqid_hash;
+ uint32_t next_request_id;
+};
+
+struct nhrp_reqid {
+ uint32_t request_id;
+ void (*cb)(struct nhrp_reqid *, void *);
+};
+
+extern struct nhrp_reqid_pool nhrp_packet_reqid;
+extern struct nhrp_reqid_pool nhrp_event_reqid;
+
+enum nhrp_cache_type {
+ NHRP_CACHE_INVALID = 0,
+ NHRP_CACHE_INCOMPLETE,
+ NHRP_CACHE_NEGATIVE,
+ NHRP_CACHE_CACHED,
+ NHRP_CACHE_DYNAMIC,
+ NHRP_CACHE_NHS,
+ NHRP_CACHE_STATIC,
+ NHRP_CACHE_LOCAL,
+ NHRP_CACHE_NUM_TYPES
+};
+
+extern const char * const nhrp_cache_type_str[];
+extern unsigned long nhrp_cache_counts[NHRP_CACHE_NUM_TYPES];
+
+struct nhrp_cache {
+ struct interface *ifp;
+ union sockunion remote_addr;
+
+ unsigned map : 1;
+ unsigned used : 1;
+ unsigned route_installed : 1;
+ unsigned nhrp_route_installed : 1;
+
+ struct notifier_block peer_notifier;
+ struct notifier_block newpeer_notifier;
+ struct notifier_list notifier_list;
+ struct nhrp_reqid eventid;
+ struct thread *t_timeout;
+ struct thread *t_auth;
+
+ struct {
+ enum nhrp_cache_type type;
+ union sockunion remote_nbma_natoa;
+ struct nhrp_peer *peer;
+ time_t expires;
+ uint32_t mtu;
+ } cur, new;
+};
+
+struct nhrp_shortcut {
+ struct prefix *p;
+ union sockunion addr;
+
+ struct nhrp_reqid reqid;
+ struct thread *t_timer;
+
+ enum nhrp_cache_type type;
+ unsigned int holding_time;
+ unsigned route_installed : 1;
+ unsigned expiring : 1;
+
+ struct nhrp_cache *cache;
+ struct notifier_block cache_notifier;
+};
+
+struct nhrp_nhs {
+ struct interface *ifp;
+ struct list_head nhslist_entry;
+
+ unsigned hub : 1;
+ afi_t afi;
+ union sockunion proto_addr;
+ const char *nbma_fqdn; /* IP-address or FQDN */
+
+ struct thread *t_resolve;
+ struct resolver_query dns_resolve;
+ struct list_head reglist_head;
+};
+
+#define NHRP_IFF_SHORTCUT 0x0001
+#define NHRP_IFF_REDIRECT 0x0002
+#define NHRP_IFF_REG_NO_UNIQUE 0x0100
+
+struct nhrp_interface {
+ struct interface *ifp;
+
+ unsigned enabled : 1;
+
+ char *ipsec_profile, *ipsec_fallback_profile, *source;
+ union sockunion nbma;
+ union sockunion nat_nbma;
+ unsigned int linkidx;
+ uint32_t grekey;
+
+ struct hash *peer_hash;
+ struct hash *cache_hash;
+
+ struct notifier_list notifier_list;
+
+ struct interface *nbmaifp;
+ struct notifier_block nbmanifp_notifier;
+
+ struct nhrp_afi_data {
+ unsigned flags;
+ unsigned short configured : 1;
+ union sockunion addr;
+ uint32_t network_id;
+ short configured_mtu;
+ unsigned short mtu;
+ unsigned int holdtime;
+ struct list_head nhslist_head;
+ } afi[AFI_MAX];
+};
+
+int sock_open_unix(const char *path);
+
+void nhrp_interface_init(void);
+void nhrp_interface_update(struct interface *ifp);
+void nhrp_interface_update_mtu(struct interface *ifp, afi_t afi);
+
+int nhrp_interface_add(int cmd, struct zclient *client, zebra_size_t length, vrf_id_t vrf_id);
+int nhrp_interface_delete(int cmd, struct zclient *client, zebra_size_t length, vrf_id_t vrf_id);
+int nhrp_interface_up(int cmd, struct zclient *client, zebra_size_t length, vrf_id_t vrf_id);
+int nhrp_interface_down(int cmd, struct zclient *client, zebra_size_t length, vrf_id_t vrf_id);
+int nhrp_interface_address_add(int cmd, struct zclient *client, zebra_size_t length, vrf_id_t vrf_id);
+int nhrp_interface_address_delete(int cmd, struct zclient *client, zebra_size_t length, vrf_id_t vrf_id);
+
+void nhrp_interface_notify_add(struct interface *ifp, struct notifier_block *n, notifier_fn_t fn);
+void nhrp_interface_notify_del(struct interface *ifp, struct notifier_block *n);
+void nhrp_interface_set_protection(struct interface *ifp, const char *profile, const char *fallback_profile);
+void nhrp_interface_set_source(struct interface *ifp, const char *ifname);
+
+int nhrp_nhs_add(struct interface *ifp, afi_t afi, union sockunion *proto_addr, const char *nbma_fqdn);
+int nhrp_nhs_del(struct interface *ifp, afi_t afi, union sockunion *proto_addr, const char *nbma_fqdn);
+int nhrp_nhs_free(struct nhrp_nhs *nhs);
+void nhrp_nhs_terminate(void);
+
+void nhrp_route_update_nhrp(const struct prefix *p, struct interface *ifp);
+void nhrp_route_announce(int add, enum nhrp_cache_type type, const struct prefix *p, struct interface *ifp, const union sockunion *nexthop, uint32_t mtu);
+int nhrp_route_read(int command, struct zclient *zclient, zebra_size_t length, vrf_id_t vrf_id);
+int nhrp_route_get_nexthop(const union sockunion *addr, struct prefix *p, union sockunion *via, struct interface **ifp);
+enum nhrp_route_type nhrp_route_address(struct interface *in_ifp, union sockunion *addr, struct prefix *p, struct nhrp_peer **peer);
+
+void nhrp_config_init(void);
+
+void nhrp_shortcut_init(void);
+void nhrp_shortcut_terminate(void);
+void nhrp_shortcut_initiate(union sockunion *addr);
+void nhrp_shortcut_foreach(afi_t afi, void (*cb)(struct nhrp_shortcut *, void *), void *ctx);
+void nhrp_shortcut_purge(struct nhrp_shortcut *s, int force);
+void nhrp_shortcut_prefix_change(const struct prefix *p, int deleted);
+
+struct nhrp_cache *nhrp_cache_get(struct interface *ifp, union sockunion *remote_addr, int create);
+void nhrp_cache_foreach(struct interface *ifp, void (*cb)(struct nhrp_cache *, void *), void *ctx);
+void nhrp_cache_set_used(struct nhrp_cache *, int);
+int nhrp_cache_update_binding(struct nhrp_cache *, enum nhrp_cache_type type, int holding_time, struct nhrp_peer *p, uint32_t mtu, union sockunion *nbma_natoa);
+void nhrp_cache_notify_add(struct nhrp_cache *c, struct notifier_block *, notifier_fn_t);
+void nhrp_cache_notify_del(struct nhrp_cache *c, struct notifier_block *);
+
+void nhrp_vc_init(void);
+void nhrp_vc_terminate(void);
+struct nhrp_vc *nhrp_vc_get(const union sockunion *src, const union sockunion *dst, int create);
+int nhrp_vc_ipsec_updown(uint32_t child_id, struct nhrp_vc *vc);
+void nhrp_vc_notify_add(struct nhrp_vc *, struct notifier_block *, notifier_fn_t);
+void nhrp_vc_notify_del(struct nhrp_vc *, struct notifier_block *);
+void nhrp_vc_foreach(void (*cb)(struct nhrp_vc *, void *), void *ctx);
+void nhrp_vc_reset(void);
+
+void vici_init(void);
+void vici_terminate(void);
+void vici_request_vc(const char *profile, union sockunion *src, union sockunion *dst, int prio);
+
+extern const char *nhrp_event_socket_path;
+
+void evmgr_init(void);
+void evmgr_terminate(void);
+void evmgr_set_socket(const char *socket);
+void evmgr_notify(const char *name, struct nhrp_cache *c, void (*cb)(struct nhrp_reqid *, void *));
+
+struct nhrp_packet_header *nhrp_packet_push(
+ struct zbuf *zb, uint8_t type,
+ const union sockunion *src_nbma,
+ const union sockunion *src_proto,
+ const union sockunion *dst_proto);
+void nhrp_packet_complete(struct zbuf *zb, struct nhrp_packet_header *hdr);
+uint16_t nhrp_packet_calculate_checksum(const uint8_t *pdu, uint16_t len);
+
+struct nhrp_packet_header *nhrp_packet_pull(
+ struct zbuf *zb,
+ union sockunion *src_nbma,
+ union sockunion *src_proto,
+ union sockunion *dst_proto);
+
+struct nhrp_cie_header *nhrp_cie_push(
+ struct zbuf *zb, uint8_t code,
+ const union sockunion *nbma,
+ const union sockunion *proto);
+struct nhrp_cie_header *nhrp_cie_pull(
+ struct zbuf *zb,
+ struct nhrp_packet_header *hdr,
+ union sockunion *nbma,
+ union sockunion *proto);
+
+struct nhrp_extension_header *nhrp_ext_push(struct zbuf *zb, struct nhrp_packet_header *hdr, uint16_t type);
+void nhrp_ext_complete(struct zbuf *zb, struct nhrp_extension_header *ext);
+struct nhrp_extension_header *nhrp_ext_pull(struct zbuf *zb, struct zbuf *payload);
+void nhrp_ext_request(struct zbuf *zb, struct nhrp_packet_header *hdr, struct interface *);
+int nhrp_ext_reply(struct zbuf *zb, struct nhrp_packet_header *hdr, struct interface *ifp, struct nhrp_extension_header *ext, struct zbuf *extpayload);
+
+uint32_t nhrp_reqid_alloc(struct nhrp_reqid_pool *, struct nhrp_reqid *r, void (*cb)(struct nhrp_reqid *, void *));
+void nhrp_reqid_free(struct nhrp_reqid_pool *, struct nhrp_reqid *r);
+struct nhrp_reqid *nhrp_reqid_lookup(struct nhrp_reqid_pool *, uint32_t reqid);
+
+int nhrp_packet_init(void);
+
+struct nhrp_peer *nhrp_peer_get(struct interface *ifp, const union sockunion *remote_nbma);
+struct nhrp_peer *nhrp_peer_ref(struct nhrp_peer *p);
+void nhrp_peer_unref(struct nhrp_peer *p);
+int nhrp_peer_check(struct nhrp_peer *p, int establish);
+void nhrp_peer_notify_add(struct nhrp_peer *p, struct notifier_block *, notifier_fn_t);
+void nhrp_peer_notify_del(struct nhrp_peer *p, struct notifier_block *);
+void nhrp_peer_recv(struct nhrp_peer *p, struct zbuf *zb);
+void nhrp_peer_send(struct nhrp_peer *p, struct zbuf *zb);
+void nhrp_peer_send_indication(struct interface *ifp, uint16_t, struct zbuf *);
+
+#endif
diff --git a/nhrpd/os.h b/nhrpd/os.h
new file mode 100644
index 0000000000..0fbe8b003f
--- /dev/null
+++ b/nhrpd/os.h
@@ -0,0 +1,5 @@
+
+int os_socket(void);
+int os_sendmsg(const uint8_t *buf, size_t len, int ifindex, const uint8_t *addr, size_t addrlen);
+int os_recvmsg(uint8_t *buf, size_t *len, int *ifindex, uint8_t *addr, size_t *addrlen);
+int os_configure_dmvpn(unsigned int ifindex, const char *ifname, int af);
diff --git a/nhrpd/reqid.c b/nhrpd/reqid.c
new file mode 100644
index 0000000000..24b3199397
--- /dev/null
+++ b/nhrpd/reqid.c
@@ -0,0 +1,49 @@
+#include "zebra.h"
+#include "hash.h"
+#include "nhrpd.h"
+
+static unsigned int nhrp_reqid_key(void *data)
+{
+ struct nhrp_reqid *r = data;
+ return r->request_id;
+}
+
+static int nhrp_reqid_cmp(const void *data, const void *key)
+{
+ const struct nhrp_reqid *a = data, *b = key;
+ return a->request_id == b->request_id;
+}
+
+uint32_t nhrp_reqid_alloc(struct nhrp_reqid_pool *p, struct nhrp_reqid *r, void (*cb)(struct nhrp_reqid *, void *))
+{
+ if (!p->reqid_hash) {
+ p->reqid_hash = hash_create(nhrp_reqid_key, nhrp_reqid_cmp);
+ p->next_request_id = 1;
+ }
+
+ if (r->cb != cb) {
+ r->request_id = p->next_request_id;
+ if (++p->next_request_id == 0) p->next_request_id = 1;
+ r->cb = cb;
+ hash_get(p->reqid_hash, r, hash_alloc_intern);
+ }
+ return r->request_id;
+}
+
+void nhrp_reqid_free(struct nhrp_reqid_pool *p, struct nhrp_reqid *r)
+{
+ if (r->cb) {
+ hash_release(p->reqid_hash, r);
+ r->cb = NULL;
+ }
+}
+
+struct nhrp_reqid *nhrp_reqid_lookup(struct nhrp_reqid_pool *p, uint32_t reqid)
+{
+ struct nhrp_reqid key;
+ if (!p->reqid_hash) return 0;
+ key.request_id = reqid;
+ return hash_lookup(p->reqid_hash, &key);
+}
+
+
diff --git a/nhrpd/resolver.c b/nhrpd/resolver.c
new file mode 100644
index 0000000000..07bdb735a3
--- /dev/null
+++ b/nhrpd/resolver.c
@@ -0,0 +1,190 @@
+/* C-Ares integration to Quagga mainloop
+ * Copyright (c) 2014-2015 Timo Teräs
+ *
+ * This file is free software: you may copy, redistribute 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.
+ */
+
+#include <ares.h>
+#include <ares_version.h>
+
+#include "vector.h"
+#include "thread.h"
+#include "nhrpd.h"
+
+struct resolver_state {
+ ares_channel channel;
+ struct thread *timeout;
+ vector read_threads, write_threads;
+};
+
+static struct resolver_state state;
+
+#define THREAD_RUNNING ((struct thread *)-1)
+
+static void resolver_update_timeouts(struct resolver_state *r);
+
+static int resolver_cb_timeout(struct thread *t)
+{
+ struct resolver_state *r = THREAD_ARG(t);
+
+ r->timeout = THREAD_RUNNING;
+ ares_process(r->channel, NULL, NULL);
+ r->timeout = NULL;
+ resolver_update_timeouts(r);
+
+ return 0;
+}
+
+static int resolver_cb_socket_readable(struct thread *t)
+{
+ struct resolver_state *r = THREAD_ARG(t);
+ int fd = THREAD_FD(t);
+
+ vector_set_index(r->read_threads, fd, THREAD_RUNNING);
+ ares_process_fd(r->channel, fd, ARES_SOCKET_BAD);
+ if (vector_lookup(r->read_threads, fd) == THREAD_RUNNING) {
+ t = NULL;
+ THREAD_READ_ON(master, t, resolver_cb_socket_readable, r, fd);
+ vector_set_index(r->read_threads, fd, t);
+ }
+ resolver_update_timeouts(r);
+
+ return 0;
+}
+
+static int resolver_cb_socket_writable(struct thread *t)
+{
+ struct resolver_state *r = THREAD_ARG(t);
+ int fd = THREAD_FD(t);
+
+ vector_set_index(r->write_threads, fd, THREAD_RUNNING);
+ ares_process_fd(r->channel, ARES_SOCKET_BAD, fd);
+ if (vector_lookup(r->write_threads, fd) == THREAD_RUNNING) {
+ t = NULL;
+ THREAD_WRITE_ON(master, t, resolver_cb_socket_writable, r, fd);
+ vector_set_index(r->write_threads, fd, t);
+ }
+ resolver_update_timeouts(r);
+
+ return 0;
+}
+
+static void resolver_update_timeouts(struct resolver_state *r)
+{
+ struct timeval *tv, tvbuf;
+
+ if (r->timeout == THREAD_RUNNING) return;
+
+ THREAD_OFF(r->timeout);
+ tv = ares_timeout(r->channel, NULL, &tvbuf);
+ if (tv) {
+ unsigned int timeoutms = tv->tv_sec * 1000 + tv->tv_usec / 1000;
+ THREAD_TIMER_MSEC_ON(master, r->timeout, resolver_cb_timeout, r, timeoutms);
+ }
+}
+
+static void ares_socket_cb(void *data, ares_socket_t fd, int readable, int writable)
+{
+ struct resolver_state *r = (struct resolver_state *) data;
+ struct thread *t;
+
+ if (readable) {
+ t = vector_lookup_ensure(r->read_threads, fd);
+ if (!t) {
+ THREAD_READ_ON(master, t, resolver_cb_socket_readable, r, fd);
+ vector_set_index(r->read_threads, fd, t);
+ }
+ } else {
+ t = vector_lookup(r->read_threads, fd);
+ if (t) {
+ if (t != THREAD_RUNNING) {
+ THREAD_OFF(t);
+ }
+ vector_unset(r->read_threads, fd);
+ }
+ }
+
+ if (writable) {
+ t = vector_lookup_ensure(r->write_threads, fd);
+ if (!t) {
+ THREAD_READ_ON(master, t, resolver_cb_socket_writable, r, fd);
+ vector_set_index(r->write_threads, fd, t);
+ }
+ } else {
+ t = vector_lookup(r->write_threads, fd);
+ if (t) {
+ if (t != THREAD_RUNNING) {
+ THREAD_OFF(t);
+ }
+ vector_unset(r->write_threads, fd);
+ }
+ }
+}
+
+void resolver_init(void)
+{
+ struct ares_options ares_opts;
+
+ state.read_threads = vector_init(1);
+ state.write_threads = vector_init(1);
+
+ ares_opts = (struct ares_options) {
+ .sock_state_cb = &ares_socket_cb,
+ .sock_state_cb_data = &state,
+ .timeout = 2,
+ .tries = 3,
+ };
+
+ ares_init_options(&state.channel, &ares_opts,
+ ARES_OPT_SOCK_STATE_CB | ARES_OPT_TIMEOUT |
+ ARES_OPT_TRIES);
+}
+
+
+static void ares_address_cb(void *arg, int status, int timeouts, struct hostent *he)
+{
+ struct resolver_query *query = (struct resolver_query *) arg;
+ union sockunion addr[16];
+ size_t i;
+
+ if (status != ARES_SUCCESS) {
+ debugf(NHRP_DEBUG_COMMON, "[%p] Resolving failed", query);
+ query->callback(query, -1, NULL);
+ query->callback = NULL;
+ return;
+ }
+
+ for (i = 0; he->h_addr_list[i] != NULL && i < ZEBRA_NUM_OF(addr); i++) {
+ memset(&addr[i], 0, sizeof(addr[i]));
+ addr[i].sa.sa_family = he->h_addrtype;
+ switch (he->h_addrtype) {
+ case AF_INET:
+ memcpy(&addr[i].sin.sin_addr, (uint8_t *) he->h_addr_list[i], he->h_length);
+ break;
+ case AF_INET6:
+ memcpy(&addr[i].sin6.sin6_addr, (uint8_t *) he->h_addr_list[i], he->h_length);
+ break;
+ }
+ }
+
+ debugf(NHRP_DEBUG_COMMON, "[%p] Resolved with %d results", query, (int) i);
+ query->callback(query, i, &addr[0]);
+ query->callback = NULL;
+}
+
+void resolver_resolve(struct resolver_query *query, int af, const char *hostname, void (*callback)(struct resolver_query *, int, union sockunion *))
+{
+ if (query->callback != NULL) {
+ zlog_err("Trying to resolve '%s', but previous query was not finished yet", hostname);
+ return;
+ }
+
+ debugf(NHRP_DEBUG_COMMON, "[%p] Resolving '%s'", query, hostname);
+
+ query->callback = callback;
+ ares_gethostbyname(state.channel, hostname, af, ares_address_cb, query);
+ resolver_update_timeouts(&state);
+}
diff --git a/nhrpd/vici.c b/nhrpd/vici.c
new file mode 100644
index 0000000000..507dd14a9c
--- /dev/null
+++ b/nhrpd/vici.c
@@ -0,0 +1,482 @@
+/* strongSwan VICI protocol implementation for NHRP
+ * Copyright (c) 2014-2015 Timo Teräs
+ *
+ * This file is free software: you may copy, redistribute 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.
+ */
+
+#include <string.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+
+#include "thread.h"
+#include "zbuf.h"
+#include "log.h"
+#include "nhrpd.h"
+
+#include "vici.h"
+
+#define ERRNO_IO_RETRY(EN) (((EN) == EAGAIN) || ((EN) == EWOULDBLOCK) || ((EN) == EINTR))
+
+struct blob {
+ char *ptr;
+ int len;
+};
+
+static int blob_equal(const struct blob *b, const char *str)
+{
+ if (b->len != (int) strlen(str)) return 0;
+ return memcmp(b->ptr, str, b->len) == 0;
+}
+
+static int blob2buf(const struct blob *b, char *buf, size_t n)
+{
+ if (b->len >= (int) n) return 0;
+ memcpy(buf, b->ptr, b->len);
+ buf[b->len] = 0;
+ return 1;
+}
+
+struct vici_conn {
+ struct thread *t_reconnect, *t_read, *t_write;
+ struct zbuf ibuf;
+ struct zbuf_queue obuf;
+ int fd;
+ uint8_t ibuf_data[VICI_MAX_MSGLEN];
+};
+
+struct vici_message_ctx {
+ const char *sections[8];
+ int nsections;
+};
+
+static int vici_reconnect(struct thread *t);
+static void vici_submit_request(struct vici_conn *vici, const char *name, ...);
+
+static void vici_zbuf_puts(struct zbuf *obuf, const char *str)
+{
+ size_t len = strlen(str);
+ zbuf_put8(obuf, len);
+ zbuf_put(obuf, str, len);
+}
+
+static void vici_connection_error(struct vici_conn *vici)
+{
+ nhrp_vc_reset();
+
+ THREAD_OFF(vici->t_read);
+ THREAD_OFF(vici->t_write);
+ zbuf_reset(&vici->ibuf);
+ zbufq_reset(&vici->obuf);
+
+ close(vici->fd);
+ vici->fd = -1;
+ THREAD_TIMER_ON(master, vici->t_reconnect, vici_reconnect, vici, 2);
+}
+
+static void vici_parse_message(
+ struct vici_conn *vici, struct zbuf *msg,
+ void (*parser)(struct vici_message_ctx *ctx, enum vici_type_t msgtype, const struct blob *key, const struct blob *val),
+ struct vici_message_ctx *ctx)
+{
+ uint8_t *type;
+ struct blob key;
+ struct blob val;
+
+ while ((type = zbuf_may_pull(msg, uint8_t)) != NULL) {
+ switch (*type) {
+ case VICI_SECTION_START:
+ key.len = zbuf_get8(msg);
+ key.ptr = zbuf_pulln(msg, key.len);
+ debugf(NHRP_DEBUG_VICI, "VICI: Section start '%.*s'", key.len, key.ptr);
+ parser(ctx, *type, &key, NULL);
+ ctx->nsections++;
+ break;
+ case VICI_SECTION_END:
+ debugf(NHRP_DEBUG_VICI, "VICI: Section end");
+ parser(ctx, *type, NULL, NULL);
+ ctx->nsections--;
+ break;
+ case VICI_KEY_VALUE:
+ key.len = zbuf_get8(msg);
+ key.ptr = zbuf_pulln(msg, key.len);
+ val.len = zbuf_get_be16(msg);
+ val.ptr = zbuf_pulln(msg, val.len);
+ debugf(NHRP_DEBUG_VICI, "VICI: Key '%.*s'='%.*s'", key.len, key.ptr, val.len, val.ptr);
+ parser(ctx, *type, &key, &val);
+ break;
+ case VICI_LIST_START:
+ key.len = zbuf_get8(msg);
+ key.ptr = zbuf_pulln(msg, key.len);
+ debugf(NHRP_DEBUG_VICI, "VICI: List start '%.*s'", key.len, key.ptr);
+ break;
+ case VICI_LIST_ITEM:
+ val.len = zbuf_get_be16(msg);
+ val.ptr = zbuf_pulln(msg, val.len);
+ debugf(NHRP_DEBUG_VICI, "VICI: List item: '%.*s'", val.len, val.ptr);
+ parser(ctx, *type, &key, &val);
+ break;
+ case VICI_LIST_END:
+ debugf(NHRP_DEBUG_VICI, "VICI: List end");
+ break;
+ default:
+ debugf(NHRP_DEBUG_VICI, "VICI: Unsupported message component type %d", *type);
+ return;
+ }
+ }
+}
+
+struct handle_sa_ctx {
+ struct vici_message_ctx msgctx;
+ int event;
+ int child_ok;
+ int kill_ikesa;
+ uint32_t child_uniqueid, ike_uniqueid;
+ struct {
+ union sockunion host;
+ struct blob id, cert;
+ } local, remote;
+};
+
+static void parse_sa_message(
+ struct vici_message_ctx *ctx,
+ enum vici_type_t msgtype,
+ const struct blob *key, const struct blob *val)
+{
+ struct handle_sa_ctx *sactx = container_of(ctx, struct handle_sa_ctx, msgctx);
+ struct nhrp_vc *vc;
+ char buf[512];
+
+ switch (msgtype) {
+ case VICI_SECTION_START:
+ if (ctx->nsections == 3) {
+ /* Begin of child-sa section, reset child vars */
+ sactx->child_uniqueid = 0;
+ sactx->child_ok = 0;
+ }
+ break;
+ case VICI_SECTION_END:
+ if (ctx->nsections == 3) {
+ /* End of child-sa section, update nhrp_vc */
+ int up = sactx->child_ok || sactx->event == 1;
+ if (up) {
+ vc = nhrp_vc_get(&sactx->local.host, &sactx->remote.host, up);
+ if (vc) {
+ blob2buf(&sactx->local.id, vc->local.id, sizeof(vc->local.id));
+ if (blob2buf(&sactx->local.cert, (char*)vc->local.cert, sizeof(vc->local.cert)))
+ vc->local.certlen = sactx->local.cert.len;
+ blob2buf(&sactx->remote.id, vc->remote.id, sizeof(vc->remote.id));
+ if (blob2buf(&sactx->remote.cert, (char*)vc->remote.cert, sizeof(vc->remote.cert)))
+ vc->remote.certlen = sactx->remote.cert.len;
+ sactx->kill_ikesa |= nhrp_vc_ipsec_updown(sactx->child_uniqueid, vc);
+ }
+ } else {
+ nhrp_vc_ipsec_updown(sactx->child_uniqueid, 0);
+ }
+ }
+ break;
+ default:
+ switch (key->ptr[0]) {
+ case 'l':
+ if (blob_equal(key, "local-host") && ctx->nsections == 1) {
+ if (blob2buf(val, buf, sizeof(buf)))
+ str2sockunion(buf, &sactx->local.host);
+ } else if (blob_equal(key, "local-id") && ctx->nsections == 1) {
+ sactx->local.id = *val;
+ } else if (blob_equal(key, "local-cert-data") && ctx->nsections == 1) {
+ sactx->local.cert = *val;
+ }
+ break;
+ case 'r':
+ if (blob_equal(key, "remote-host") && ctx->nsections == 1) {
+ if (blob2buf(val, buf, sizeof(buf)))
+ str2sockunion(buf, &sactx->remote.host);
+ } else if (blob_equal(key, "remote-id") && ctx->nsections == 1) {
+ sactx->remote.id = *val;
+ } else if (blob_equal(key, "remote-cert-data") && ctx->nsections == 1) {
+ sactx->remote.cert = *val;
+ }
+ break;
+ case 'u':
+ if (blob_equal(key, "uniqueid") && blob2buf(val, buf, sizeof(buf))) {
+ if (ctx->nsections == 3)
+ sactx->child_uniqueid = strtoul(buf, NULL, 0);
+ else if (ctx->nsections == 1)
+ sactx->ike_uniqueid = strtoul(buf, NULL, 0);
+ }
+ break;
+ case 's':
+ if (blob_equal(key, "state") && ctx->nsections == 3) {
+ sactx->child_ok =
+ (sactx->event == 0 &&
+ (blob_equal(val, "INSTALLED") ||
+ blob_equal(val, "REKEYED")));
+ }
+ break;
+ }
+ break;
+ }
+}
+
+static void vici_recv_sa(struct vici_conn *vici, struct zbuf *msg, int event)
+{
+ char buf[32];
+ struct handle_sa_ctx ctx = {
+ .event = event,
+ };
+
+ vici_parse_message(vici, msg, parse_sa_message, &ctx.msgctx);
+
+ if (ctx.kill_ikesa && ctx.ike_uniqueid) {
+ debugf(NHRP_DEBUG_COMMON, "VICI: Deleting IKE_SA %u", ctx.ike_uniqueid);
+ snprintf(buf, sizeof buf, "%u", ctx.ike_uniqueid);
+ vici_submit_request(
+ vici, "terminate",
+ VICI_KEY_VALUE, "ike-id", strlen(buf), buf,
+ VICI_END);
+ }
+}
+
+static void vici_recv_message(struct vici_conn *vici, struct zbuf *msg)
+{
+ uint32_t msglen;
+ uint8_t msgtype;
+ struct blob name;
+
+ msglen = zbuf_get_be32(msg);
+ msgtype = zbuf_get8(msg);
+ debugf(NHRP_DEBUG_VICI, "VICI: Message %d, %d bytes", msgtype, msglen);
+
+ switch (msgtype) {
+ case VICI_EVENT:
+ name.len = zbuf_get8(msg);
+ name.ptr = zbuf_pulln(msg, name.len);
+
+ debugf(NHRP_DEBUG_VICI, "VICI: Event '%.*s'", name.len, name.ptr);
+ if (blob_equal(&name, "list-sa") ||
+ blob_equal(&name, "child-updown") ||
+ blob_equal(&name, "child-rekey"))
+ vici_recv_sa(vici, msg, 0);
+ else if (blob_equal(&name, "child-state-installed") ||
+ blob_equal(&name, "child-state-rekeyed"))
+ vici_recv_sa(vici, msg, 1);
+ else if (blob_equal(&name, "child-state-destroying"))
+ vici_recv_sa(vici, msg, 2);
+ break;
+ case VICI_EVENT_UNKNOWN:
+ zlog_err("VICI: StrongSwan does not support mandatory events (unpatched?)");
+ break;
+ case VICI_EVENT_CONFIRM:
+ case VICI_CMD_RESPONSE:
+ break;
+ default:
+ zlog_notice("VICI: Unrecognized message type %d", msgtype);
+ break;
+ }
+}
+
+static int vici_read(struct thread *t)
+{
+ struct vici_conn *vici = THREAD_ARG(t);
+ struct zbuf *ibuf = &vici->ibuf;
+ struct zbuf pktbuf;
+
+ vici->t_read = NULL;
+ if (zbuf_read(ibuf, vici->fd, (size_t) -1) < 0) {
+ vici_connection_error(vici);
+ return 0;
+ }
+
+ /* Process all messages in buffer */
+ do {
+ uint32_t *hdrlen = zbuf_may_pull(ibuf, uint32_t);
+ if (!hdrlen)
+ break;
+ if (!zbuf_may_pulln(ibuf, ntohl(*hdrlen))) {
+ zbuf_reset_head(ibuf, hdrlen);
+ break;
+ }
+
+ /* Handle packet */
+ zbuf_init(&pktbuf, hdrlen, htonl(*hdrlen)+4, htonl(*hdrlen)+4);
+ vici_recv_message(vici, &pktbuf);
+ } while (1);
+
+ THREAD_READ_ON(master, vici->t_read, vici_read, vici, vici->fd);
+ return 0;
+}
+
+static int vici_write(struct thread *t)
+{
+ struct vici_conn *vici = THREAD_ARG(t);
+ int r;
+
+ vici->t_write = NULL;
+ r = zbufq_write(&vici->obuf, vici->fd);
+ if (r > 0) {
+ THREAD_WRITE_ON(master, vici->t_write, vici_write, vici, vici->fd);
+ } else if (r < 0) {
+ vici_connection_error(vici);
+ }
+
+ return 0;
+}
+
+static void vici_submit(struct vici_conn *vici, struct zbuf *obuf)
+{
+ if (vici->fd < 0) {
+ zbuf_free(obuf);
+ return;
+ }
+
+ zbufq_queue(&vici->obuf, obuf);
+ THREAD_WRITE_ON(master, vici->t_write, vici_write, vici, vici->fd);
+}
+
+static void vici_submit_request(struct vici_conn *vici, const char *name, ...)
+{
+ struct zbuf *obuf;
+ uint32_t *hdrlen;
+ va_list va;
+ size_t len;
+ int type;
+
+ obuf = zbuf_alloc(256);
+ if (!obuf) return;
+
+ hdrlen = zbuf_push(obuf, uint32_t);
+ zbuf_put8(obuf, VICI_CMD_REQUEST);
+ vici_zbuf_puts(obuf, name);
+
+ va_start(va, name);
+ for (type = va_arg(va, int); type != VICI_END; type = va_arg(va, int)) {
+ zbuf_put8(obuf, type);
+ switch (type) {
+ case VICI_KEY_VALUE:
+ vici_zbuf_puts(obuf, va_arg(va, const char *));
+ len = va_arg(va, size_t);
+ zbuf_put_be16(obuf, len);
+ zbuf_put(obuf, va_arg(va, void *), len);
+ break;
+ case VICI_END:
+ break;
+ default:
+ break;
+ }
+ }
+ va_end(va);
+ *hdrlen = htonl(zbuf_used(obuf) - 4);
+ vici_submit(vici, obuf);
+}
+
+static void vici_register_event(struct vici_conn *vici, const char *name)
+{
+ struct zbuf *obuf;
+ uint32_t *hdrlen;
+ uint8_t namelen;
+
+ namelen = strlen(name);
+ obuf = zbuf_alloc(4 + 1 + 1 + namelen);
+ if (!obuf) return;
+
+ hdrlen = zbuf_push(obuf, uint32_t);
+ zbuf_put8(obuf, VICI_EVENT_REGISTER);
+ zbuf_put8(obuf, namelen);
+ zbuf_put(obuf, name, namelen);
+ *hdrlen = htonl(zbuf_used(obuf) - 4);
+
+ vici_submit(vici, obuf);
+}
+
+static int vici_reconnect(struct thread *t)
+{
+ struct vici_conn *vici = THREAD_ARG(t);
+ int fd;
+
+ vici->t_reconnect = NULL;
+ if (vici->fd >= 0) return 0;
+
+ fd = sock_open_unix("/var/run/charon.vici");
+ if (fd < 0) {
+ zlog_warn("%s: failure connecting VICI socket: %s",
+ __PRETTY_FUNCTION__, strerror(errno));
+ THREAD_TIMER_ON(master, vici->t_reconnect, vici_reconnect, vici, 2);
+ return 0;
+ }
+
+ debugf(NHRP_DEBUG_COMMON, "VICI: Connected");
+ vici->fd = fd;
+ THREAD_READ_ON(master, vici->t_read, vici_read, vici, vici->fd);
+
+ /* Send event subscribtions */
+ //vici_register_event(vici, "child-updown");
+ //vici_register_event(vici, "child-rekey");
+ vici_register_event(vici, "child-state-installed");
+ vici_register_event(vici, "child-state-rekeyed");
+ vici_register_event(vici, "child-state-destroying");
+ vici_register_event(vici, "list-sa");
+ vici_submit_request(vici, "list-sas", VICI_END);
+
+ return 0;
+}
+
+static struct vici_conn vici_connection;
+
+void vici_init(void)
+{
+ struct vici_conn *vici = &vici_connection;
+
+ vici->fd = -1;
+ zbuf_init(&vici->ibuf, vici->ibuf_data, sizeof(vici->ibuf_data), 0);
+ zbufq_init(&vici->obuf);
+ THREAD_TIMER_MSEC_ON(master, vici->t_reconnect, vici_reconnect, vici, 10);
+}
+
+void vici_terminate(void)
+{
+}
+
+void vici_request_vc(const char *profile, union sockunion *src, union sockunion *dst, int prio)
+{
+ struct vici_conn *vici = &vici_connection;
+ char buf[2][SU_ADDRSTRLEN];
+
+ sockunion2str(src, buf[0], sizeof buf[0]);
+ sockunion2str(dst, buf[1], sizeof buf[1]);
+
+ vici_submit_request(
+ vici, "initiate",
+ VICI_KEY_VALUE, "child", strlen(profile), profile,
+ VICI_KEY_VALUE, "timeout", 2, "-1",
+ VICI_KEY_VALUE, "async", 1, "1",
+ VICI_KEY_VALUE, "init-limits", 1, prio ? "0" : "1",
+ VICI_KEY_VALUE, "my-host", strlen(buf[0]), buf[0],
+ VICI_KEY_VALUE, "other-host", strlen(buf[1]), buf[1],
+ VICI_END);
+}
+
+int sock_open_unix(const char *path)
+{
+ int ret, fd;
+ struct sockaddr_un addr;
+
+ fd = socket(AF_UNIX, SOCK_STREAM, 0);
+ if (fd < 0)
+ return -1;
+
+ memset(&addr, 0, sizeof (struct sockaddr_un));
+ addr.sun_family = AF_UNIX;
+ strncpy(addr.sun_path, path, strlen (path));
+
+ ret = connect(fd, (struct sockaddr *) &addr, sizeof(addr.sun_family) + strlen(addr.sun_path));
+ if (ret < 0) {
+ close(fd);
+ return -1;
+ }
+
+ fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) | O_NONBLOCK);
+
+ return fd;
+}
diff --git a/nhrpd/vici.h b/nhrpd/vici.h
new file mode 100644
index 0000000000..24b900b43c
--- /dev/null
+++ b/nhrpd/vici.h
@@ -0,0 +1,24 @@
+
+enum vici_type_t {
+ VICI_START = 0,
+ VICI_SECTION_START = 1,
+ VICI_SECTION_END = 2,
+ VICI_KEY_VALUE = 3,
+ VICI_LIST_START = 4,
+ VICI_LIST_ITEM = 5,
+ VICI_LIST_END = 6,
+ VICI_END = 7
+};
+
+enum vici_operation_t {
+ VICI_CMD_REQUEST = 0,
+ VICI_CMD_RESPONSE,
+ VICI_CMD_UNKNOWN,
+ VICI_EVENT_REGISTER,
+ VICI_EVENT_UNREGISTER,
+ VICI_EVENT_CONFIRM,
+ VICI_EVENT_UNKNOWN,
+ VICI_EVENT,
+};
+
+#define VICI_MAX_MSGLEN (512*1024)
diff --git a/nhrpd/zbuf.c b/nhrpd/zbuf.c
new file mode 100644
index 0000000000..97962b9ab8
--- /dev/null
+++ b/nhrpd/zbuf.c
@@ -0,0 +1,220 @@
+/* Stream/packet buffer API implementation
+ * Copyright (c) 2014-2015 Timo Teräs
+ *
+ * This file is free software: you may copy, redistribute 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.
+ */
+
+#define _GNU_SOURCE
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include "zassert.h"
+#include "zbuf.h"
+#include "memory.h"
+#include "nhrpd.h"
+
+#define ERRNO_IO_RETRY(EN) (((EN) == EAGAIN) || ((EN) == EWOULDBLOCK) || ((EN) == EINTR))
+
+DEFINE_MTYPE_STATIC(NHRPD, ZBUF_DATA, "NHRPD zbuf data")
+
+struct zbuf *zbuf_alloc(size_t size)
+{
+ struct zbuf *zb;
+
+ zb = XMALLOC(MTYPE_ZBUF_DATA, sizeof(*zb) + size);
+ if (!zb)
+ return NULL;
+
+ zbuf_init(zb, zb+1, size, 0);
+ zb->allocated = 1;
+
+ return zb;
+}
+
+void zbuf_init(struct zbuf *zb, void *buf, size_t len, size_t datalen)
+{
+ *zb = (struct zbuf) {
+ .buf = buf,
+ .end = (uint8_t *)buf + len,
+ .head = buf,
+ .tail = (uint8_t *)buf + datalen,
+ };
+}
+
+void zbuf_free(struct zbuf *zb)
+{
+ if (zb->allocated)
+ XFREE(MTYPE_ZBUF_DATA, zb);
+}
+
+void zbuf_reset(struct zbuf *zb)
+{
+ zb->head = zb->tail = zb->buf;
+ zb->error = 0;
+}
+
+void zbuf_reset_head(struct zbuf *zb, void *ptr)
+{
+ zassert((void*)zb->buf <= ptr && ptr <= (void*)zb->tail);
+ zb->head = ptr;
+}
+
+static void zbuf_remove_headroom(struct zbuf *zb)
+{
+ ssize_t headroom = zbuf_headroom(zb);
+ if (!headroom)
+ return;
+ memmove(zb->buf, zb->head, zbuf_used(zb));
+ zb->head -= headroom;
+ zb->tail -= headroom;
+}
+
+ssize_t zbuf_read(struct zbuf *zb, int fd, size_t maxlen)
+{
+ ssize_t r;
+
+ if (zb->error)
+ return -3;
+
+ zbuf_remove_headroom(zb);
+ if (maxlen > zbuf_tailroom(zb))
+ maxlen = zbuf_tailroom(zb);
+
+ r = read(fd, zb->tail, maxlen);
+ if (r > 0) zb->tail += r;
+ else if (r == 0) r = -2;
+ else if (r < 0 && ERRNO_IO_RETRY(errno)) r = 0;
+
+ return r;
+}
+
+ssize_t zbuf_write(struct zbuf *zb, int fd)
+{
+ ssize_t r;
+
+ if (zb->error)
+ return -3;
+
+ r = write(fd, zb->head, zbuf_used(zb));
+ if (r > 0) {
+ zb->head += r;
+ if (zb->head == zb->tail)
+ zbuf_reset(zb);
+ }
+ else if (r == 0) r = -2;
+ else if (r < 0 && ERRNO_IO_RETRY(errno)) r = 0;
+
+ return r;
+}
+
+ssize_t zbuf_recv(struct zbuf *zb, int fd)
+{
+ ssize_t r;
+
+ if (zb->error)
+ return -3;
+
+ zbuf_remove_headroom(zb);
+ r = recv(fd, zb->tail, zbuf_tailroom(zb), 0);
+ if (r > 0) zb->tail += r;
+ else if (r == 0) r = -2;
+ else if (r < 0 && ERRNO_IO_RETRY(errno)) r = 0;
+ return r;
+}
+
+ssize_t zbuf_send(struct zbuf *zb, int fd)
+{
+ ssize_t r;
+
+ if (zb->error)
+ return -3;
+
+ r = send(fd, zb->head, zbuf_used(zb), 0);
+ if (r >= 0)
+ zbuf_reset(zb);
+
+ return r;
+}
+
+void *zbuf_may_pull_until(struct zbuf *zb, const char *sep, struct zbuf *msg)
+{
+ size_t seplen = strlen(sep), len;
+ uint8_t *ptr;
+
+ ptr = memmem(zb->head, zbuf_used(zb), sep, seplen);
+ if (!ptr) return NULL;
+
+ len = ptr - zb->head + seplen;
+ zbuf_init(msg, zbuf_pulln(zb, len), len, len);
+ return msg->head;
+}
+
+void zbufq_init(struct zbuf_queue *zbq)
+{
+ *zbq = (struct zbuf_queue) {
+ .queue_head = LIST_INITIALIZER(zbq->queue_head),
+ };
+}
+
+void zbufq_reset(struct zbuf_queue *zbq)
+{
+ struct zbuf *buf, *bufn;
+
+ list_for_each_entry_safe(buf, bufn, &zbq->queue_head, queue_list) {
+ list_del(&buf->queue_list);
+ zbuf_free(buf);
+ }
+}
+
+void zbufq_queue(struct zbuf_queue *zbq, struct zbuf *zb)
+{
+ list_add_tail(&zb->queue_list, &zbq->queue_head);
+}
+
+int zbufq_write(struct zbuf_queue *zbq, int fd)
+{
+ struct iovec iov[16];
+ struct zbuf *zb, *zbn;
+ ssize_t r;
+ size_t iovcnt = 0;
+
+ list_for_each_entry_safe(zb, zbn, &zbq->queue_head, queue_list) {
+ iov[iovcnt++] = (struct iovec) {
+ .iov_base = zb->head,
+ .iov_len = zbuf_used(zb),
+ };
+ if (iovcnt >= ZEBRA_NUM_OF(iov))
+ break;
+ }
+
+ r = writev(fd, iov, iovcnt);
+ if (r < 0)
+ return r;
+
+ list_for_each_entry_safe(zb, zbn, &zbq->queue_head, queue_list) {
+ if (r < (ssize_t)zbuf_used(zb)) {
+ zb->head += r;
+ return 1;
+ }
+
+ r -= zbuf_used(zb);
+ list_del(&zb->queue_list);
+ zbuf_free(zb);
+ }
+
+ return 0;
+}
+
+void zbuf_copy(struct zbuf *zdst, struct zbuf *zsrc, size_t len)
+{
+ const void *src;
+ void *dst;
+
+ dst = zbuf_pushn(zdst, len);
+ src = zbuf_pulln(zsrc, len);
+ if (!dst || !src) return;
+ memcpy(dst, src, len);
+}
diff --git a/nhrpd/zbuf.h b/nhrpd/zbuf.h
new file mode 100644
index 0000000000..73d7073447
--- /dev/null
+++ b/nhrpd/zbuf.h
@@ -0,0 +1,189 @@
+/* Stream/packet buffer API
+ * Copyright (c) 2014-2015 Timo Teräs
+ *
+ * This file is free software: you may copy, redistribute 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.
+ */
+
+#ifndef ZBUF_H
+#define ZBUF_H
+
+#include <stdint.h>
+#include <string.h>
+#include <endian.h>
+#include <sys/types.h>
+
+#include "zassert.h"
+#include "list.h"
+
+struct zbuf {
+ struct list_head queue_list;
+ unsigned allocated : 1;
+ unsigned error : 1;
+ uint8_t *buf, *end;
+ uint8_t *head, *tail;
+};
+
+struct zbuf_queue {
+ struct list_head queue_head;
+};
+
+struct zbuf *zbuf_alloc(size_t size);
+void zbuf_init(struct zbuf *zb, void *buf, size_t len, size_t datalen);
+void zbuf_free(struct zbuf *zb);
+
+static inline size_t zbuf_size(struct zbuf *zb)
+{
+ return zb->end - zb->buf;
+}
+
+static inline size_t zbuf_used(struct zbuf *zb)
+{
+ return zb->tail - zb->head;
+}
+
+static inline size_t zbuf_tailroom(struct zbuf *zb)
+{
+ return zb->end - zb->tail;
+}
+
+static inline size_t zbuf_headroom(struct zbuf *zb)
+{
+ return zb->head - zb->buf;
+}
+
+void zbuf_reset(struct zbuf *zb);
+void zbuf_reset_head(struct zbuf *zb, void *ptr);
+ssize_t zbuf_read(struct zbuf *zb, int fd, size_t maxlen);
+ssize_t zbuf_write(struct zbuf *zb, int fd);
+ssize_t zbuf_recv(struct zbuf *zb, int fd);
+ssize_t zbuf_send(struct zbuf *zb, int fd);
+
+static inline void zbuf_set_rerror(struct zbuf *zb)
+{
+ zb->error = 1;
+ zb->head = zb->tail;
+}
+
+static inline void zbuf_set_werror(struct zbuf *zb)
+{
+ zb->error = 1;
+ zb->tail = zb->end;
+}
+
+static inline void *__zbuf_pull(struct zbuf *zb, size_t size, int error)
+{
+ void *head = zb->head;
+ if (size > zbuf_used(zb)) {
+ if (error) zbuf_set_rerror(zb);
+ return NULL;
+ }
+ zb->head += size;
+ return head;
+}
+
+#define zbuf_pull(zb, type) ((type *)__zbuf_pull(zb, sizeof(type), 1))
+#define zbuf_pulln(zb, sz) ((void *)__zbuf_pull(zb, sz, 1))
+#define zbuf_may_pull(zb, type) ((type *)__zbuf_pull(zb, sizeof(type), 0))
+#define zbuf_may_pulln(zb, sz) ((void *)__zbuf_pull(zb, sz, 0))
+
+void *zbuf_may_pull_until(struct zbuf *zb, const char *sep, struct zbuf *msg);
+
+static inline void zbuf_get(struct zbuf *zb, void *dst, size_t len)
+{
+ void *src = zbuf_pulln(zb, len);
+ if (src) memcpy(dst, src, len);
+}
+
+static inline uint8_t zbuf_get8(struct zbuf *zb)
+{
+ uint8_t *src = zbuf_pull(zb, uint8_t);
+ if (src) return *src;
+ return 0;
+}
+
+static inline uint32_t zbuf_get32(struct zbuf *zb)
+{
+ struct unaligned32 {
+ uint32_t value;
+ } __attribute__((packed));
+
+ struct unaligned32 *v = zbuf_pull(zb, struct unaligned32);
+ if (v) return v->value;
+ return 0;
+}
+
+static inline uint16_t zbuf_get_be16(struct zbuf *zb)
+{
+ struct unaligned16 {
+ uint16_t value;
+ } __attribute__((packed));
+
+ struct unaligned16 *v = zbuf_pull(zb, struct unaligned16);
+ if (v) return be16toh(v->value);
+ return 0;
+}
+
+static inline uint32_t zbuf_get_be32(struct zbuf *zb)
+{
+ return be32toh(zbuf_get32(zb));
+}
+
+static inline void *__zbuf_push(struct zbuf *zb, size_t size, int error)
+{
+ void *tail = zb->tail;
+ if (size > zbuf_tailroom(zb)) {
+ if (error) zbuf_set_werror(zb);
+ return NULL;
+ }
+ zb->tail += size;
+ return tail;
+}
+
+#define zbuf_push(zb, type) ((type *)__zbuf_push(zb, sizeof(type), 1))
+#define zbuf_pushn(zb, sz) ((void *)__zbuf_push(zb, sz, 1))
+#define zbuf_may_push(zb, type) ((type *)__zbuf_may_push(zb, sizeof(type), 0))
+#define zbuf_may_pushn(zb, sz) ((void *)__zbuf_push(zb, sz, 0))
+
+static inline void zbuf_put(struct zbuf *zb, const void *src, size_t len)
+{
+ void *dst = zbuf_pushn(zb, len);
+ if (dst) memcpy(dst, src, len);
+}
+
+static inline void zbuf_put8(struct zbuf *zb, uint8_t val)
+{
+ uint8_t *dst = zbuf_push(zb, uint8_t);
+ if (dst) *dst = val;
+}
+
+static inline void zbuf_put_be16(struct zbuf *zb, uint16_t val)
+{
+ struct unaligned16 {
+ uint16_t value;
+ } __attribute__((packed));
+
+ struct unaligned16 *v = zbuf_push(zb, struct unaligned16);
+ if (v) v->value = htobe16(val);
+}
+
+static inline void zbuf_put_be32(struct zbuf *zb, uint32_t val)
+{
+ struct unaligned32 {
+ uint32_t value;
+ } __attribute__((packed));
+
+ struct unaligned32 *v = zbuf_push(zb, struct unaligned32);
+ if (v) v->value = htobe32(val);
+}
+
+void zbuf_copy(struct zbuf *zb, struct zbuf *src, size_t len);
+
+void zbufq_init(struct zbuf_queue *);
+void zbufq_reset(struct zbuf_queue *);
+void zbufq_queue(struct zbuf_queue *, struct zbuf *);
+int zbufq_write(struct zbuf_queue *, int);
+
+#endif
diff --git a/nhrpd/znl.c b/nhrpd/znl.c
new file mode 100644
index 0000000000..2216d97eb8
--- /dev/null
+++ b/nhrpd/znl.c
@@ -0,0 +1,160 @@
+/* Netlink helpers for zbuf
+ * Copyright (c) 2014-2015 Timo Teräs
+ *
+ * This file is free software: you may copy, redistribute 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.
+ */
+
+#include <fcntl.h>
+#include <errno.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <linux/netlink.h>
+#include <linux/rtnetlink.h>
+
+#include "znl.h"
+
+#define ZNL_ALIGN(len) (((len)+3) & ~3)
+
+void *znl_push(struct zbuf *zb, size_t n)
+{
+ return zbuf_pushn(zb, ZNL_ALIGN(n));
+}
+
+void *znl_pull(struct zbuf *zb, size_t n)
+{
+ return zbuf_pulln(zb, ZNL_ALIGN(n));
+}
+
+struct nlmsghdr *znl_nlmsg_push(struct zbuf *zb, uint16_t type, uint16_t flags)
+{
+ struct nlmsghdr *n;
+
+ n = znl_push(zb, sizeof(*n));
+ if (!n) return NULL;
+
+ *n = (struct nlmsghdr) {
+ .nlmsg_type = type,
+ .nlmsg_flags = flags,
+ };
+ return n;
+}
+
+void znl_nlmsg_complete(struct zbuf *zb, struct nlmsghdr *n)
+{
+ n->nlmsg_len = zb->tail - (uint8_t*)n;
+}
+
+struct nlmsghdr *znl_nlmsg_pull(struct zbuf *zb, struct zbuf *payload)
+{
+ struct nlmsghdr *n;
+ size_t plen;
+
+ n = znl_pull(zb, sizeof(*n));
+ if (!n) return NULL;
+
+ plen = n->nlmsg_len - sizeof(*n);
+ zbuf_init(payload, znl_pull(zb, plen), plen, plen);
+ zbuf_may_pulln(zb, ZNL_ALIGN(plen) - plen);
+
+ return n;
+}
+
+struct rtattr *znl_rta_push(struct zbuf *zb, uint16_t type, const void *val, size_t len)
+{
+ struct rtattr *rta;
+ uint8_t *dst;
+
+ rta = znl_push(zb, ZNL_ALIGN(sizeof(*rta)) + ZNL_ALIGN(len));
+ if (!rta) return NULL;
+
+ *rta = (struct rtattr) {
+ .rta_type = type,
+ .rta_len = ZNL_ALIGN(sizeof(*rta)) + len,
+ };
+
+ dst = (uint8_t *)(rta+1);
+ memcpy(dst, val, len);
+ memset(dst+len, 0, ZNL_ALIGN(len) - len);
+
+ return rta;
+}
+
+struct rtattr *znl_rta_push_u32(struct zbuf *zb, uint16_t type, uint32_t val)
+{
+ return znl_rta_push(zb, type, &val, sizeof(val));
+}
+
+struct rtattr *znl_rta_nested_push(struct zbuf *zb, uint16_t type)
+{
+ struct rtattr *rta;
+
+ rta = znl_push(zb, sizeof(*rta));
+ if (!rta) return NULL;
+
+ *rta = (struct rtattr) {
+ .rta_type = type,
+ };
+ return rta;
+}
+
+void znl_rta_nested_complete(struct zbuf *zb, struct rtattr *rta)
+{
+ size_t len = zb->tail - (uint8_t*) rta;
+ size_t align = ZNL_ALIGN(len) - len;
+
+ if (align) {
+ void *dst = zbuf_pushn(zb, align);
+ if (dst) memset(dst, 0, align);
+ }
+ rta->rta_len = len;
+}
+
+struct rtattr *znl_rta_pull(struct zbuf *zb, struct zbuf *payload)
+{
+ struct rtattr *rta;
+ size_t plen;
+
+ rta = znl_pull(zb, sizeof(*rta));
+ if (!rta) return NULL;
+
+ if (rta->rta_len > sizeof(*rta)) {
+ plen = rta->rta_len - sizeof(*rta);
+ zbuf_init(payload, znl_pull(zb, plen), plen, plen);
+ } else {
+ zbuf_init(payload, NULL, 0, 0);
+ }
+
+ return rta;
+}
+
+int znl_open(int protocol, int groups)
+{
+ struct sockaddr_nl addr;
+ int fd, buf = 128 * 1024;
+
+ fd = socket(AF_NETLINK, SOCK_RAW, protocol);
+ if (fd < 0)
+ return -1;
+
+ fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) | O_NONBLOCK);
+ fcntl(fd, F_SETFD, FD_CLOEXEC);
+ if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &buf, sizeof(buf)) < 0)
+ goto error;
+
+ memset(&addr, 0, sizeof(addr));
+ addr.nl_family = AF_NETLINK;
+ addr.nl_groups = groups;
+ if (bind(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0)
+ goto error;
+
+ return fd;
+error:
+ close(fd);
+ return -1;
+}
+
diff --git a/nhrpd/znl.h b/nhrpd/znl.h
new file mode 100644
index 0000000000..2cd630b5d3
--- /dev/null
+++ b/nhrpd/znl.h
@@ -0,0 +1,29 @@
+/* Netlink helpers for zbuf
+ * Copyright (c) 2014-2015 Timo Teräs
+ *
+ * This file is free software: you may copy, redistribute 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.
+ */
+
+#include "zbuf.h"
+
+#define ZNL_BUFFER_SIZE 8192
+
+void *znl_push(struct zbuf *zb, size_t n);
+void *znl_pull(struct zbuf *zb, size_t n);
+
+struct nlmsghdr *znl_nlmsg_push(struct zbuf *zb, uint16_t type, uint16_t flags);
+void znl_nlmsg_complete(struct zbuf *zb, struct nlmsghdr *n);
+struct nlmsghdr *znl_nlmsg_pull(struct zbuf *zb, struct zbuf *payload);
+
+struct rtattr *znl_rta_push(struct zbuf *zb, uint16_t type, const void *val, size_t len);
+struct rtattr *znl_rta_push_u32(struct zbuf *zb, uint16_t type, uint32_t val);
+struct rtattr *znl_rta_nested_push(struct zbuf *zb, uint16_t type);
+void znl_rta_nested_complete(struct zbuf *zb, struct rtattr *rta);
+
+struct rtattr *znl_rta_pull(struct zbuf *zb, struct zbuf *payload);
+
+int znl_open(int protocol, int groups);
+
diff --git a/ospf6d/ospf6_main.c b/ospf6d/ospf6_main.c
index f85580fa59..54ae4645ad 100644
--- a/ospf6d/ospf6_main.c
+++ b/ospf6d/ospf6_main.c
@@ -39,7 +39,7 @@
#include "zclient.h"
#include "vrf.h"
#include "bfd.h"
-#include "sockopt.h"
+#include "libfrr.h"
#include "ospf6d.h"
#include "ospf6_top.h"
@@ -52,9 +52,6 @@
/* Default configuration file name for ospf6d. */
#define OSPF6_DEFAULT_CONFIG "ospf6d.conf"
-/* VTY Socket prefix */
-char vty_sock_path[MAXPATHLEN] = OSPF6_VTYSH_PATH;
-
/* Default port values. */
#define OSPF6_VTY_PORT 2606
@@ -82,68 +79,14 @@ struct zebra_privs_t ospf6d_privs =
};
/* ospf6d options, we use GNU getopt library. */
-#define OPTION_VTYSOCK 1000
struct option longopts[] =
{
- { "daemon", no_argument, NULL, 'd'},
- { "config_file", required_argument, NULL, 'f'},
- { "pid_file", required_argument, NULL, 'i'},
- { "socket", required_argument, NULL, 'z'},
- { "vty_addr", required_argument, NULL, 'A'},
- { "vty_port", required_argument, NULL, 'P'},
- { "vty_socket", required_argument, NULL, OPTION_VTYSOCK},
- { "user", required_argument, NULL, 'u'},
- { "group", required_argument, NULL, 'g'},
- { "version", no_argument, NULL, 'v'},
- { "dryrun", no_argument, NULL, 'C'},
- { "help", no_argument, NULL, 'h'},
{ 0 }
};
-/* Configuration file and directory. */
-char config_default[] = SYSCONFDIR OSPF6_DEFAULT_CONFIG;
-
-/* ospf6d program name. */
-char *progname;
-
-/* is daemon? */
-int daemon_mode = 0;
-
/* Master of threads. */
struct thread_master *master;
-/* Process ID saved for use by init system */
-const char *pid_file = PATH_OSPF6D_PID;
-
-/* Help information display. */
-static void
-usage (char *progname, int status)
-{
- if (status != 0)
- fprintf (stderr, "Try `%s --help' for more information.\n", progname);
- else
- {
- printf ("Usage : %s [OPTION...]\n\n\
-Daemon which manages OSPF version 3.\n\n\
--d, --daemon Runs in daemon mode\n\
--f, --config_file Set configuration file name\n\
--i, --pid_file Set process identifier file name\n\
--z, --socket Set path of zebra socket\n\
--A, --vty_addr Set vty's bind address\n\
--P, --vty_port Set vty's port number\n\
- --vty_socket Override vty socket path\n\
--u, --user User to run as\n\
--g, --group Group to run as\n\
--v, --version Print program version\n\
--C, --dryrun Check configuration for validity and exit\n\
--h, --help Display this help and exit\n\
-\n\
-Report bugs to %s\n", progname, FRR_BUG_ADDRESS);
- }
-
- exit (status);
-}
-
static void __attribute__ ((noreturn))
ospf6_exit (int status)
{
@@ -173,8 +116,7 @@ ospf6_exit (int status)
if (master)
thread_master_free (master);
- if (zlog_default)
- closezlog (zlog_default);
+ closezlog ();
exit (status);
}
@@ -208,7 +150,7 @@ static void
sigusr1 (void)
{
zlog_info ("SIGUSR1 received");
- zlog_rotate (NULL);
+ zlog_rotate();
}
struct quagga_signal_t ospf6_signals[] =
@@ -231,32 +173,31 @@ struct quagga_signal_t ospf6_signals[] =
},
};
+FRR_DAEMON_INFO(ospf6d, OSPF6,
+ .vty_port = OSPF6_VTY_PORT,
+
+ .proghelp = "Implementation of the OSPFv3 routing protocol.",
+
+ .signals = ospf6_signals,
+ .n_signals = array_size(ospf6_signals),
+
+ .privs = &ospf6d_privs,
+)
+
/* Main routine of ospf6d. Treatment of argument and starting ospf finite
state machine is handled here. */
int
main (int argc, char *argv[], char *envp[])
{
- char *p;
int opt;
- char *vty_addr = NULL;
- int vty_port = 0;
- char *config_file = NULL;
- struct thread thread;
- int dryrun = 0;
-
- /* Set umask before anything for security */
- umask (0027);
- /* Preserve name of myself. */
- progname = ((p = strrchr (argv[0], '/')) ? ++p : argv[0]);
-
- /* Seed random number for LSA ID */
- srandom (time(NULL));
+ frr_preinit (&ospf6d_di, argc, argv);
+ frr_opt_add ("", longopts, "");
/* Command line argument treatment. */
while (1)
{
- opt = getopt_long (argc, argv, "df:i:z:hp:A:P:u:g:vC", longopts, 0);
+ opt = frr_getopt (argc, argv, NULL);
if (opt == EOF)
break;
@@ -265,54 +206,8 @@ main (int argc, char *argv[], char *envp[])
{
case 0:
break;
- case 'd':
- daemon_mode = 1;
- break;
- case 'f':
- config_file = optarg;
- break;
- case 'A':
- vty_addr = optarg;
- break;
- case 'i':
- pid_file = optarg;
- break;
- case 'z':
- zclient_serv_path_set (optarg);
- break;
- case 'P':
- /* Deal with atoi() returning 0 on failure, and ospf6d not
- listening on ospf6d port... */
- if (strcmp(optarg, "0") == 0)
- {
- vty_port = 0;
- break;
- }
- vty_port = atoi (optarg);
- if (vty_port <= 0 || vty_port > 0xffff)
- vty_port = OSPF6_VTY_PORT;
- break;
- case OPTION_VTYSOCK:
- set_socket_path(vty_sock_path, OSPF6_VTYSH_PATH, optarg, sizeof (vty_sock_path));
- break;
- case 'u':
- ospf6d_privs.user = optarg;
- break;
- case 'g':
- ospf6d_privs.group = optarg;
- break;
- case 'v':
- print_version (progname);
- exit (0);
- break;
- case 'C':
- dryrun = 1;
- break;
- case 'h':
- usage (progname, 0);
- break;
default:
- usage (progname, 1);
+ frr_help_exit (1);
break;
}
}
@@ -320,27 +215,13 @@ main (int argc, char *argv[], char *envp[])
if (geteuid () != 0)
{
errno = EPERM;
- perror (progname);
+ perror (ospf6d_di.progname);
exit (1);
}
/* thread master */
- master = thread_master_create ();
-
- /* Initializations. */
- zlog_default = openzlog (progname, ZLOG_OSPF6, 0,
- LOG_CONS|LOG_NDELAY|LOG_PID,
- LOG_DAEMON);
- zprivs_init (&ospf6d_privs);
-#if defined(HAVE_CUMULUS)
- zlog_set_level (NULL, ZLOG_DEST_SYSLOG, zlog_default->default_lvl);
-#endif
+ master = frr_init ();
- /* initialize zebra libraries */
- signal_init (master, array_size(ospf6_signals), ospf6_signals);
- cmd_init (1);
- vty_init (master);
- memory_init ();
vrf_init ();
access_list_init ();
prefix_list_init ();
@@ -348,37 +229,8 @@ main (int argc, char *argv[], char *envp[])
/* initialize ospf6 */
ospf6_init ();
- /* parse config file */
- vty_read_config (config_file, config_default);
-
- /* Start execution only if not in dry-run mode */
- if (dryrun)
- return(0);
-
- if (daemon_mode && daemon (0, 0) < 0)
- {
- zlog_err("OSPF6d daemon failed: %s", strerror(errno));
- exit (1);
- }
-
- /* pid file create */
- pid_output (pid_file);
-
- /* Make ospf6 vty socket. */
- if (!vty_port)
- vty_port = OSPF6_VTY_PORT;
- vty_serv_sock (vty_addr, vty_port, vty_sock_path);
-
- /* Print start message */
- zlog_notice ("OSPF6d (Quagga-%s ospf6d-%s) starts: vty@%d",
- FRR_VERSION, OSPF6_DAEMON_VERSION,vty_port);
-
- /* Start finite state machine, here we go! */
- while (thread_fetch (master, &thread))
- thread_call (&thread);
-
- /* Log in case thread failed */
- zlog_warn ("Thread failed");
+ frr_config_fork ();
+ frr_run (master);
/* Not reached. */
ospf6_exit (0);
diff --git a/ospfd/ospf_ism.c b/ospfd/ospf_ism.c
index ae6d0cdbc5..9630616acc 100644
--- a/ospfd/ospf_ism.c
+++ b/ospfd/ospf_ism.c
@@ -255,8 +255,7 @@ ospf_hello_timer (struct thread *thread)
oi->t_hello = NULL;
if (IS_DEBUG_OSPF (ism, ISM_TIMERS))
- zlog (NULL, LOG_DEBUG, "ISM[%s]: Timer (Hello timer expire)",
- IF_NAME (oi));
+ zlog_debug("ISM[%s]: Timer (Hello timer expire)", IF_NAME(oi));
/* Sending hello packet. */
ospf_hello_send (oi);
@@ -276,8 +275,7 @@ ospf_wait_timer (struct thread *thread)
oi->t_wait = NULL;
if (IS_DEBUG_OSPF (ism, ISM_TIMERS))
- zlog (NULL, LOG_DEBUG, "ISM[%s]: Timer (Wait timer expire)",
- IF_NAME (oi));
+ zlog_debug("ISM[%s]: Timer (Wait timer expire)", IF_NAME(oi));
OSPF_ISM_EVENT_SCHEDULE (oi, ISM_WaitTimer);
@@ -418,7 +416,7 @@ static int
ism_ignore (struct ospf_interface *oi)
{
if (IS_DEBUG_OSPF (ism, ISM_EVENTS))
- zlog (NULL, LOG_DEBUG, "ISM[%s]: ism_ignore called", IF_NAME (oi));
+ zlog_debug("ISM[%s]: ism_ignore called", IF_NAME(oi));
return 0;
}
@@ -539,9 +537,9 @@ ism_change_state (struct ospf_interface *oi, int state)
/* Logging change of state. */
if (IS_DEBUG_OSPF (ism, ISM_STATUS))
- zlog (NULL, LOG_DEBUG, "ISM[%s]: State change %s -> %s", IF_NAME (oi),
- LOOKUP (ospf_ism_state_msg, oi->state),
- LOOKUP (ospf_ism_state_msg, state));
+ zlog_debug("ISM[%s]: State change %s -> %s", IF_NAME(oi),
+ LOOKUP(ospf_ism_state_msg, oi->state),
+ LOOKUP(ospf_ism_state_msg, state));
old_state = oi->state;
oi->state = state;
@@ -617,9 +615,9 @@ ospf_ism_event (struct thread *thread)
next_state = ISM [oi->state][event].next_state;
if (IS_DEBUG_OSPF (ism, ISM_EVENTS))
- zlog (NULL, LOG_DEBUG, "ISM[%s]: %s (%s)", IF_NAME (oi),
- LOOKUP (ospf_ism_state_msg, oi->state),
- ospf_ism_event_str[event]);
+ zlog_debug("ISM[%s]: %s (%s)", IF_NAME(oi),
+ LOOKUP(ospf_ism_state_msg, oi->state),
+ ospf_ism_event_str[event]);
/* If state is changed. */
if (next_state != oi->state)
diff --git a/ospfd/ospf_main.c b/ospfd/ospf_main.c
index edb1ca470e..5705dad5d4 100644
--- a/ospfd/ospf_main.c
+++ b/ospfd/ospf_main.c
@@ -41,7 +41,7 @@
#include "sigevent.h"
#include "zclient.h"
#include "vrf.h"
-#include "sockopt.h"
+#include "libfrr.h"
#include "ospfd/ospfd.h"
#include "ospfd/ospf_interface.h"
@@ -76,41 +76,19 @@ struct zebra_privs_t ospfd_privs =
.cap_num_i = 0
};
-/* Configuration filename and directory. */
-char config_default[100];
-
/* OSPFd options. */
-#define OPTION_VTYSOCK 1000
struct option longopts[] =
{
- { "daemon", no_argument, NULL, 'd'},
{ "instance", required_argument, NULL, 'n'},
- { "config_file", required_argument, NULL, 'f'},
- { "pid_file", required_argument, NULL, 'i'},
- { "socket", required_argument, NULL, 'z'},
- { "dryrun", no_argument, NULL, 'C'},
- { "help", no_argument, NULL, 'h'},
- { "vty_addr", required_argument, NULL, 'A'},
- { "vty_port", required_argument, NULL, 'P'},
- { "vty_socket", required_argument, NULL, OPTION_VTYSOCK},
- { "user", required_argument, NULL, 'u'},
- { "group", required_argument, NULL, 'g'},
{ "apiserver", no_argument, NULL, 'a'},
- { "version", no_argument, NULL, 'v'},
{ 0 }
};
/* OSPFd program name */
-/* VTY Socket prefix */
-char vty_sock_path[MAXPATHLEN] = OSPF_VTYSH_PATH;
-
/* Master of threads. */
struct thread_master *master;
-/* Process ID saved for use by init system */
-char pid_file[100];
-
#ifdef SUPPORT_OSPF_API
extern int ospf_apiserver_enable;
#endif /* SUPPORT_OSPF_API */
@@ -124,21 +102,6 @@ usage (char *progname, int status)
else
{
printf ("Usage : %s [OPTION...]\n\
-Daemon which manages OSPF.\n\n\
--d, --daemon Runs in daemon mode\n\
--n, --instance Set the instance id\n\
--f, --config_file Set configuration file name\n\
--i, --pid_file Set process identifier file name\n\
--z, --socket Set path of zebra socket\n\
--A, --vty_addr Set vty's bind address\n\
--P, --vty_port Set vty's port number\n\
- --vty_socket Override vty socket path\n\
--u, --user User to run as\n\
--g, --group Group to run as\n\
--a. --apiserver Enable OSPF apiserver\n\
--v, --version Print program version\n\
--C, --dryrun Check configuration for validity and exit\n\
--h, --help Display this help and exit\n\
\n\
Report bugs to %s\n", progname, FRR_BUG_ADDRESS);
}
@@ -149,7 +112,7 @@ Report bugs to %s\n", progname, FRR_BUG_ADDRESS);
static void
sighup (void)
{
- zlog (NULL, LOG_INFO, "SIGHUP received");
+ zlog_info("SIGHUP received");
}
/* SIGINT / SIGTERM handler. */
@@ -164,7 +127,7 @@ sigint (void)
static void
sigusr1 (void)
{
- zlog_rotate (NULL);
+ zlog_rotate();
}
struct quagga_signal_t ospf_signals[] =
@@ -187,39 +150,38 @@ struct quagga_signal_t ospf_signals[] =
},
};
+FRR_DAEMON_INFO(ospfd, OSPF,
+ .vty_port = OSPF_VTY_PORT,
+
+ .proghelp = "Implementation of the OSPFv2 routing protocol.",
+
+ .signals = ospf_signals,
+ .n_signals = array_size(ospf_signals),
+
+ .privs = &ospfd_privs,
+)
+
/* OSPFd main routine. */
int
main (int argc, char **argv)
{
- char *p;
- char *vty_addr = NULL;
- int vty_port = OSPF_VTY_PORT;
- char vty_path[100];
- int daemon_mode = 0;
- char *config_file = NULL;
- char *progname;
u_short instance = 0;
- struct thread thread;
- int dryrun = 0;
-
- /* Set umask before anything for security */
- umask (0027);
#ifdef SUPPORT_OSPF_API
/* OSPF apiserver is disabled by default. */
ospf_apiserver_enable = 0;
#endif /* SUPPORT_OSPF_API */
- strcpy(pid_file, PATH_OSPFD_PID);
-
- /* get program name */
- progname = ((p = strrchr (argv[0], '/')) ? ++p : argv[0]);
+ frr_preinit (&ospfd_di, argc, argv);
+ frr_opt_add ("n:a", longopts,
+ " -n, --instance Set the instance id\n"
+ " -a, --apiserver Enable OSPF apiserver\n");
while (1)
{
int opt;
- opt = getopt_long (argc, argv, "df:i:n:z:hA:P:u:g:avC", longopts, 0);
+ opt = frr_getopt (argc, argv, NULL);
if (opt == EOF)
break;
@@ -227,65 +189,19 @@ main (int argc, char **argv)
switch (opt)
{
case 'n':
- instance = atoi(optarg);
+ ospfd_di.instance = instance = atoi(optarg);
if (instance < 1)
exit(0);
break;
case 0:
break;
- case 'd':
- daemon_mode = 1;
- break;
- case 'f':
- config_file = optarg;
- break;
- case 'A':
- vty_addr = optarg;
- break;
- case 'i':
- strcpy(pid_file,optarg);
- break;
- case 'z':
- zclient_serv_path_set (optarg);
- break;
- case 'P':
- /* Deal with atoi() returning 0 on failure, and ospfd not
- listening on ospfd port... */
- if (strcmp(optarg, "0") == 0)
- {
- vty_port = 0;
- break;
- }
- vty_port = atoi (optarg);
- if (vty_port <= 0 || vty_port > 0xffff)
- vty_port = OSPF_VTY_PORT;
- break;
- case OPTION_VTYSOCK:
- set_socket_path(vty_sock_path, OSPF_VTYSH_PATH, optarg, sizeof (vty_sock_path));
- break;
- case 'u':
- ospfd_privs.user = optarg;
- break;
- case 'g':
- ospfd_privs.group = optarg;
- break;
#ifdef SUPPORT_OSPF_API
case 'a':
ospf_apiserver_enable = 1;
break;
#endif /* SUPPORT_OSPF_API */
- case 'v':
- print_version (progname);
- exit (0);
- break;
- case 'C':
- dryrun = 1;
- break;
- case 'h':
- usage (progname, 0);
- break;
default:
- usage (progname, 1);
+ frr_help_exit (1);
break;
}
}
@@ -294,29 +210,18 @@ main (int argc, char **argv)
if (geteuid () != 0)
{
errno = EPERM;
- perror (progname);
+ perror (ospfd_di.progname);
exit (1);
}
- zlog_default = openzlog (progname, ZLOG_OSPF, instance,
- LOG_CONS|LOG_NDELAY|LOG_PID, LOG_DAEMON);
- zprivs_init (&ospfd_privs);
-#if defined(HAVE_CUMULUS)
- zlog_set_level (NULL, ZLOG_DEST_SYSLOG, zlog_default->default_lvl);
-#endif
-
/* OSPF master init. */
- ospf_master_init ();
+ ospf_master_init (frr_init ());
/* Initializations. */
master = om->master;
/* Library inits. */
- signal_init (master, array_size(ospf_signals), ospf_signals);
- cmd_init (1);
debug_init ();
- vty_init (master);
- memory_init ();
vrf_init ();
access_list_init ();
@@ -349,74 +254,8 @@ main (int argc, char **argv)
exit (1);
}
- /* Get configuration file. */
- if (instance)
- sprintf(config_default, "%sospfd-%d.conf", SYSCONFDIR, instance);
- else
- sprintf(config_default, "%s%s", SYSCONFDIR, OSPF_DEFAULT_CONFIG);
- vty_read_config (config_file, config_default);
-
- /* Start execution only if not in dry-run mode */
- if (dryrun)
- return(0);
-
- /* Change to the daemon program. */
- if (daemon_mode && daemon (0, 0) < 0)
- {
- zlog_err("OSPFd daemon failed: %s", strerror(errno));
- exit (1);
- }
-
- /* Create PID file */
- if (instance)
- {
- char pidfile_temp[100];
-
- /* Override the single file with file including instance
- number in case of multi-instance */
- if (strrchr(pid_file, '/') != NULL)
- /* cut of pid_file at last / char * to get directory */
- *strrchr(pid_file, '/') = '\0';
- else
- /* pid_file contains no directory - should never happen, but deal with it anyway */
- /* throw-away all pid_file and assume it's only the filename */
- pid_file[0] = '\0';
-
- snprintf(pidfile_temp, sizeof(pidfile_temp), "%s/ospfd-%d.pid", pid_file, instance );
- strlcpy(pid_file, pidfile_temp, sizeof(pid_file));
- }
- /* Process id file create. */
- pid_output (pid_file);
-
- /* Create VTY socket */
- if (instance)
- {
- /* Multi-Instance. Use only path section of vty_sock_path with new file incl instance */
- if (strrchr(vty_sock_path, '/') != NULL)
- {
- /* cut of pid_file at last / char * to get directory */
- *strrchr(vty_sock_path, '/') = '\0';
- }
- else
- {
- /* pid_file contains no directory - should never happen, but deal with it anyway */
- /* throw-away all pid_file and assume it's only the filename */
- vty_sock_path[0] = '\0';
- }
- snprintf(vty_path, sizeof(vty_path), "%s/ospfd-%d.vty", vty_sock_path, instance );
- }
- else
- {
- strlcpy(vty_path, vty_sock_path, sizeof(vty_path));
- }
- vty_serv_sock (vty_addr, vty_port, vty_path);
-
- /* Print banner. */
- zlog_notice ("OSPFd %s starting: vty@%d, %s", FRR_VERSION, vty_port, vty_path);
-
- /* Fetch next active thread. */
- while (thread_fetch (master, &thread))
- thread_call (&thread);
+ frr_config_fork();
+ frr_run (master);
/* Not reached. */
return (0);
diff --git a/ospfd/ospf_nsm.c b/ospfd/ospf_nsm.c
index ccb82bf8fe..01617055ce 100644
--- a/ospfd/ospf_nsm.c
+++ b/ospfd/ospf_nsm.c
@@ -63,8 +63,8 @@ ospf_inactivity_timer (struct thread *thread)
nbr->t_inactivity = NULL;
if (IS_DEBUG_OSPF (nsm, NSM_TIMERS))
- zlog (NULL, LOG_DEBUG, "NSM[%s:%s]: Timer (Inactivity timer expire)",
- IF_NAME (nbr->oi), inet_ntoa (nbr->router_id));
+ zlog_debug("NSM[%s:%s]: Timer (Inactivity timer expire)",
+ IF_NAME(nbr->oi), inet_ntoa(nbr->router_id));
OSPF_NSM_EVENT_SCHEDULE (nbr, NSM_InactivityTimer);
@@ -80,8 +80,8 @@ ospf_db_desc_timer (struct thread *thread)
nbr->t_db_desc = NULL;
if (IS_DEBUG_OSPF (nsm, NSM_TIMERS))
- zlog (NULL, LOG_DEBUG, "NSM[%s:%s]: Timer (DD Retransmit timer expire)",
- IF_NAME (nbr->oi), inet_ntoa (nbr->src));
+ zlog_debug("NSM[%s:%s]: Timer (DD Retransmit timer expire)",
+ IF_NAME(nbr->oi), inet_ntoa(nbr->src));
/* resent last send DD packet. */
assert (nbr->last_send);
diff --git a/ospfd/ospf_packet.c b/ospfd/ospf_packet.c
index bf78336ad5..a0cc367cd7 100644
--- a/ospfd/ospf_packet.c
+++ b/ospfd/ospf_packet.c
@@ -2971,9 +2971,8 @@ ospf_read (struct thread *thread)
ospf_ls_ack (iph, ospfh, ibuf, oi, length);
break;
default:
- zlog (NULL, LOG_WARNING,
- "interface %s: OSPF packet header type %d is illegal",
- IF_NAME (oi), ospfh->type);
+ zlog_warn("interface %s: OSPF packet header type %d is illegal",
+ IF_NAME(oi), ospfh->type);
break;
}
@@ -3453,8 +3452,8 @@ ospf_poll_timer (struct thread *thread)
nbr_nbma->t_poll = NULL;
if (IS_DEBUG_OSPF (nsm, NSM_TIMERS))
- zlog (NULL, LOG_DEBUG, "NSM[%s:%s]: Timer (Poll timer expire)",
- IF_NAME (nbr_nbma->oi), inet_ntoa (nbr_nbma->addr));
+ zlog_debug("NSM[%s:%s]: Timer (Poll timer expire)", IF_NAME(nbr_nbma->oi),
+ inet_ntoa(nbr_nbma->addr));
ospf_poll_send (nbr_nbma);
@@ -3477,8 +3476,8 @@ ospf_hello_reply_timer (struct thread *thread)
assert (nbr->oi);
if (IS_DEBUG_OSPF (nsm, NSM_TIMERS))
- zlog (NULL, LOG_DEBUG, "NSM[%s:%s]: Timer (hello-reply timer expire)",
- IF_NAME (nbr->oi), inet_ntoa (nbr->router_id));
+ zlog_debug("NSM[%s:%s]: Timer (hello-reply timer expire)",
+ IF_NAME(nbr->oi), inet_ntoa(nbr->router_id));
ospf_hello_send_sub (nbr->oi, nbr->address.u.prefix4.s_addr);
diff --git a/ospfd/ospf_snmp.c b/ospfd/ospf_snmp.c
index 676756d6d9..4afbda8788 100644
--- a/ospfd/ospf_snmp.c
+++ b/ospfd/ospf_snmp.c
@@ -2658,8 +2658,8 @@ ospfTrapNbrStateChange (struct ospf_neighbor *on)
char msgbuf[16];
ospf_nbr_state_message(on, msgbuf, sizeof(msgbuf));
- zlog (NULL, LOG_INFO, "ospfTrapNbrStateChange trap sent: %s now %s",
- inet_ntoa(on->address.u.prefix4), msgbuf);
+ zlog_info("ospfTrapNbrStateChange trap sent: %s now %s",
+ inet_ntoa(on->address.u.prefix4), msgbuf);
oid_copy_addr (index, &(on->address.u.prefix4), IN_ADDR_SIZE);
index[IN_ADDR_SIZE] = 0;
@@ -2678,7 +2678,7 @@ ospfTrapVirtNbrStateChange (struct ospf_neighbor *on)
{
oid index[sizeof (oid) * (IN_ADDR_SIZE + 1)];
- zlog (NULL, LOG_INFO, "ospfTrapVirtNbrStateChange trap sent");
+ zlog_info("ospfTrapVirtNbrStateChange trap sent");
oid_copy_addr (index, &(on->address.u.prefix4), IN_ADDR_SIZE);
index[IN_ADDR_SIZE] = 0;
@@ -2697,9 +2697,9 @@ ospfTrapIfStateChange (struct ospf_interface *oi)
{
oid index[sizeof (oid) * (IN_ADDR_SIZE + 1)];
- zlog (NULL, LOG_INFO, "ospfTrapIfStateChange trap sent: %s now %s",
- inet_ntoa(oi->address->u.prefix4),
- LOOKUP(ospf_ism_state_msg, oi->state));
+ zlog_info("ospfTrapIfStateChange trap sent: %s now %s",
+ inet_ntoa(oi->address->u.prefix4),
+ LOOKUP(ospf_ism_state_msg, oi->state));
oid_copy_addr (index, &(oi->address->u.prefix4), IN_ADDR_SIZE);
index[IN_ADDR_SIZE] = 0;
@@ -2718,7 +2718,7 @@ ospfTrapVirtIfStateChange (struct ospf_interface *oi)
{
oid index[sizeof (oid) * (IN_ADDR_SIZE + 1)];
- zlog (NULL, LOG_INFO, "ospfTrapVirtIfStateChange trap sent");
+ zlog_info("ospfTrapVirtIfStateChange trap sent");
oid_copy_addr (index, &(oi->address->u.prefix4), IN_ADDR_SIZE);
index[IN_ADDR_SIZE] = 0;
diff --git a/ospfd/ospf_vty.c b/ospfd/ospf_vty.c
index 62498f4c82..ce4bc9dfaa 100644
--- a/ospfd/ospf_vty.c
+++ b/ospfd/ospf_vty.c
@@ -9117,7 +9117,7 @@ ospf_interface_clear (struct interface *ifp)
if (!if_is_operative (ifp)) return;
if (IS_DEBUG_OSPF (ism, ISM_EVENTS))
- zlog (NULL, LOG_DEBUG, "ISM[%s]: clear by reset", ifp->name);
+ zlog_debug("ISM[%s]: clear by reset", ifp->name);
ospf_if_reset(ifp);
}
diff --git a/ospfd/ospfd.c b/ospfd/ospfd.c
index 570734bd8c..7cd3903678 100644
--- a/ospfd/ospfd.c
+++ b/ospfd/ospfd.c
@@ -1917,11 +1917,11 @@ ospf_nbr_nbma_poll_interval_unset (struct ospf *ospf, struct in_addr addr)
}
void
-ospf_master_init ()
+ospf_master_init (struct thread_master *master)
{
memset (&ospf_master, 0, sizeof (struct ospf_master));
om = &ospf_master;
om->ospf = list_new ();
- om->master = thread_master_create ();
+ om->master = master;
}
diff --git a/ospfd/ospfd.h b/ospfd/ospfd.h
index 8ebd3c4ec6..a3bd0ca12f 100644
--- a/ospfd/ospfd.h
+++ b/ospfd/ospfd.h
@@ -576,7 +576,7 @@ extern void ospf_area_del_if (struct ospf_area *, struct ospf_interface *);
extern void ospf_route_map_init (void);
extern void ospf_snmp_init (void);
-extern void ospf_master_init (void);
+extern void ospf_master_init (struct thread_master *master);
extern int ospf_interface_set (struct interface *ifp, struct in_addr area_id);
extern int ospf_interface_unset (struct interface *ifp);
diff --git a/pimd/pim_main.c b/pimd/pim_main.c
index 2e81ac53fd..ba1c511d16 100644
--- a/pimd/pim_main.c
+++ b/pimd/pim_main.c
@@ -38,7 +38,7 @@
#include "prefix.h"
#include "plist.h"
#include "vrf.h"
-#include "sockopt.h"
+#include "libfrr.h"
#include "pimd.h"
#include "pim_version.h"
@@ -50,27 +50,10 @@
extern struct host host;
-char config_default[] = SYSCONFDIR PIMD_DEFAULT_CONFIG;
-
-/* pimd options */
-#define OPTION_VTYSOCK 1000
struct option longopts[] = {
- { "daemon", no_argument, NULL, 'd'},
- { "config_file", required_argument, NULL, 'f'},
- { "pid_file", required_argument, NULL, 'i'},
- { "socket", required_argument, NULL, 'z'},
- { "vty_addr", required_argument, NULL, 'A'},
- { "vty_port", required_argument, NULL, 'P'},
- { "vty_socket", required_argument, NULL, OPTION_VTYSOCK},
- { "version", no_argument, NULL, 'v'},
- { "debug_zclient", no_argument, NULL, 'Z'},
- { "help", no_argument, NULL, 'h'},
{ 0 }
};
-/* VTY Socket prefix */
-char vty_sock_path[MAXPATHLEN] = PIM_VTYSH_PATH;
-
/* pimd privileges */
zebra_capabilities_t _caps_p [] =
{
@@ -95,101 +78,40 @@ struct zebra_privs_t pimd_privs =
.cap_num_i = 0
};
-char* progname;
-const char *pid_file = PATH_PIMD_PID;
+FRR_DAEMON_INFO(pimd, PIM,
+ .vty_port = PIMD_VTY_PORT,
-static void usage(int status)
-{
- if (status != 0)
- fprintf (stderr, "Try `%s --help' for more information.\n", progname);
- else {
- printf ("Usage : %s [OPTION...]\n\
-Daemon which manages PIM.\n\n\
--d, --daemon Run in daemon mode\n\
--f, --config_file Set configuration file name\n\
--i, --pid_file Set process identifier file name\n\
--z, --socket Set path of zebra socket\n\
--A, --vty_addr Set vty's bind address\n\
--P, --vty_port Set vty's port number\n\
- --vty_socket Override vty socket path\n\
--v, --version Print program version\n\
--h, --help Display this help and exit\n\
-\n\
-Report bugs to %s\n", progname, PACKAGE_BUGREPORT);
- }
+ .proghelp = "Implementation of the PIM routing protocol.",
- exit (status);
-}
+ .signals = pimd_signals,
+ .n_signals = 4 /* XXX array_size(pimd_signals) XXX*/,
+ .privs = &pimd_privs,
+)
int main(int argc, char** argv, char** envp) {
- char *p;
- char *vty_addr = NULL;
- int vty_port = -1;
- int daemon_mode = 0;
- char *config_file = NULL;
- char *zebra_sock_path = NULL;
- struct thread thread;
-
- umask(0027);
-
- progname = ((p = strrchr(argv[0], '/')) ? ++p : argv[0]);
-
- zlog_default = openzlog(progname, ZLOG_PIM, 0,
- LOG_CONS|LOG_NDELAY|LOG_PID, LOG_DAEMON);
- zprivs_init (&pimd_privs);
-#if defined(HAVE_CUMULUS)
- zlog_set_level (NULL, ZLOG_DEST_SYSLOG, zlog_default->default_lvl);
-#endif
+ frr_preinit(&pimd_di, argc, argv);
+ frr_opt_add("", longopts, "");
- /* this while just reads the options */
+ /* this while just reads the options */
while (1) {
int opt;
-
- opt = getopt_long (argc, argv, "df:i:z:A:P:vZh", longopts, 0);
-
+
+ opt = frr_getopt(argc, argv, NULL);
+
if (opt == EOF)
break;
switch (opt) {
case 0:
break;
- case 'd':
- daemon_mode = 1;
- break;
- case 'f':
- config_file = optarg;
- break;
- case 'i':
- pid_file = optarg;
- break;
- case 'z':
- zebra_sock_path = optarg;
- break;
- case 'A':
- vty_addr = optarg;
- break;
- case 'P':
- vty_port = atoi (optarg);
- break;
- case OPTION_VTYSOCK:
- set_socket_path(vty_sock_path, PIM_VTYSH_PATH, optarg, sizeof (vty_sock_path));
- break;
- case 'v':
- printf(PIMD_PROGNAME " version %s\n", PIMD_VERSION);
- print_version(progname);
- exit (0);
- break;
- case 'h':
- usage (0);
- break;
default:
- usage (1);
+ frr_help_exit (1);
break;
}
}
- master = thread_master_create();
+ master = frr_init();
zlog_notice("Quagga %s " PIMD_PROGNAME " %s starting",
FRR_VERSION, PIMD_VERSION);
@@ -197,10 +119,6 @@ int main(int argc, char** argv, char** envp) {
/*
* Initializations
*/
- pim_signals_init();
- cmd_init(1);
- vty_init(master);
- memory_init();
vrf_init ();
access_list_init();
prefix_list_init ();
@@ -214,36 +132,9 @@ int main(int argc, char** argv, char** envp) {
/*
* Initialize zclient "update" and "lookup" sockets
*/
- pim_zebra_init(zebra_sock_path);
-
- zlog_notice("Loading configuration - begin");
-
- /* Get configuration file. */
- vty_read_config(config_file, config_default);
-
- /*
- Starting from here zlog_* functions will log according configuration
- */
+ pim_zebra_init();
- zlog_notice("Loading configuration - end");
-
- /* Change to the daemon program. */
- if (daemon_mode) {
- if (daemon(0, 0)) {
- zlog_warn("failed to daemonize");
- }
- }
-
- /* Process ID file creation. */
- pid_output(pid_file);
-
- /* Create pimd VTY socket */
- if (vty_port < 0)
- vty_port = PIMD_VTY_PORT;
- vty_serv_sock(vty_addr, vty_port, vty_sock_path);
-
- zlog_notice("Quagga %s " PIMD_PROGNAME " %s starting, VTY interface at port TCP %d",
- FRR_VERSION, PIMD_VERSION, vty_port);
+ frr_config_fork();
#ifdef PIM_DEBUG_BYDEFAULT
zlog_notice("PIM_DEBUG_BYDEFAULT: Enabling all debug commands");
@@ -267,11 +158,7 @@ int main(int argc, char** argv, char** envp) {
zlog_notice("PIM_UNEXPECTED_KERNEL_UPCALL: report unexpected kernel upcall");
#endif
- while (thread_fetch(master, &thread))
- thread_call(&thread);
-
- zlog_err("%s %s: thread_fetch() returned NULL, exiting",
- __FILE__, __PRETTY_FUNCTION__);
+ frr_run(master);
/* never reached */
return 0;
diff --git a/pimd/pim_signals.c b/pimd/pim_signals.c
index bd4d9e4857..053ef6a67b 100644
--- a/pimd/pim_signals.c
+++ b/pimd/pim_signals.c
@@ -55,10 +55,10 @@ static void pim_sigterm()
static void pim_sigusr1()
{
- zlog_rotate (NULL);
+ zlog_rotate();
}
-static struct quagga_signal_t pimd_signals[] =
+struct quagga_signal_t pimd_signals[] =
{
{
.signal = SIGHUP,
@@ -77,9 +77,3 @@ static struct quagga_signal_t pimd_signals[] =
.handler = &pim_sigterm,
},
};
-
-void pim_signals_init()
-{
- signal_init(master, array_size(pimd_signals), pimd_signals);
-}
-
diff --git a/pimd/pim_signals.h b/pimd/pim_signals.h
index 7b25608c10..d7a4926d4d 100644
--- a/pimd/pim_signals.h
+++ b/pimd/pim_signals.h
@@ -21,6 +21,7 @@
#ifndef PIM_SIGNALS_H
#define PIM_SIGNALS_H
-void pim_signals_init(void);
+#include "sigevent.h"
+extern struct quagga_signal_t pimd_signals[];
#endif /* PIM_SIGNALS_H */
diff --git a/pimd/pim_zebra.c b/pimd/pim_zebra.c
index 70b0302806..1db6616c58 100644
--- a/pimd/pim_zebra.c
+++ b/pimd/pim_zebra.c
@@ -721,13 +721,10 @@ pim_zebra_connected (struct zclient *zclient)
zclient_send_reg_requests (zclient, VRF_DEFAULT);
}
-void pim_zebra_init(char *zebra_sock_path)
+void pim_zebra_init(void)
{
int i;
- if (zebra_sock_path)
- zclient_serv_path_set(zebra_sock_path);
-
#ifdef HAVE_TCP_ZEBRA
zlog_notice("zclient update contacting ZEBRA daemon at socket TCP %s,%d", "127.0.0.1", ZEBRA_PORT);
#else
diff --git a/pimd/pim_zebra.h b/pimd/pim_zebra.h
index 476185def1..751a7be25d 100644
--- a/pimd/pim_zebra.h
+++ b/pimd/pim_zebra.h
@@ -24,7 +24,7 @@
#include "pim_igmp.h"
#include "pim_ifchannel.h"
-void pim_zebra_init(char *zebra_sock_path);
+void pim_zebra_init(void);
void pim_zebra_zclient_update (struct vty *vty);
void pim_scan_individual_oil (struct channel_oil *c_oil);
diff --git a/pimd/pimd.c b/pimd/pimd.c
index aa863fd47f..c8a0efc401 100644
--- a/pimd/pimd.c
+++ b/pimd/pimd.c
@@ -98,8 +98,6 @@ static void pim_free()
void pim_init()
{
- srandom(time(NULL));
-
qpim_rp_keep_alive_time = PIM_RP_KEEPALIVE_PERIOD;
pim_rp_init ();
diff --git a/redhat/frr.spec.in b/redhat/frr.spec.in
index c53e8459c9..bb23931b13 100644
--- a/redhat/frr.spec.in
+++ b/redhat/frr.spec.in
@@ -18,6 +18,7 @@
%{!?with_rtadv: %global with_rtadv 1 }
%{!?with_mpls: %global with_mpls 0 }
%{!?with_ldpd: %global with_ldpd 0 }
+%{!?with_nhrpd: %global with_nhrpd 1 }
%{!?with_shared: %global with_shared 1 }
%{!?with_multipath: %global with_multipath 256 }
%{!?frr_user: %global frr_user frr }
@@ -80,13 +81,19 @@
%define daemon_ldpd ""
%endif
+%if %{with_nhrpd}
+%define daemon_nhrpd nhrpd
+%else
+%define daemon_nhrpd ""
+%endif
+
%if %{with_watchfrr}
%define daemon_watchfrr watchfrr
%else
%define daemon_watchfrr ""
%endif
-%define all_daemons %{daemon_list} %{daemon_ldpd} %{daemon_watchfrr}
+%define all_daemons %{daemon_list} %{daemon_ldpd} %{daemon_nhrpd} %{daemon_watchfrr}
# allow build dir to be kept
%{!?keep_build: %global keep_build 0 }
@@ -132,8 +139,8 @@ FreeRangeRouting is a free software that manages TCP/IP based routing
protocol. It takes multi-server and multi-thread approach to resolve
the current complexity of the Internet.
-FreeRangeRouting supports BGP4, OSPFv2, OSPFv3, ISIS, RIP, RIPng, PIM
-and LDP
+FreeRangeRouting supports BGP4, OSPFv2, OSPFv3, ISIS, RIP, RIPng, PIM,
+LDP and NHRP.
FreeRangeRouting is a fork of Quagga.
@@ -215,6 +222,11 @@ developing OSPF-API and frr applications.
%else
--disable-ldpd \
%endif
+%if %{with_nhrpd}
+ --enable-nhrpd \
+%else
+ --disable-nhrpd \
+%endif
%if %{with_pam}
--with-libpam \
%endif
@@ -336,6 +348,9 @@ zebra_spec_add_service ospf6d 2606/tcp "OSPF6d vty"
zebra_spec_add_service ospfapi 2607/tcp "OSPF-API"
%endif
zebra_spec_add_service isisd 2608/tcp "ISISd vty"
+%if %{with_nhrpd}
+zebra_spec_add_service nhrpd 2610/tcp "NHRPd vty"
+%endif
zebra_spec_add_service pimd 2611/tcp "PIMd vty"
%if %{with_ldpd}
zebra_spec_add_service ldpd 2612/tcp "LDPd vty"
@@ -521,6 +536,9 @@ rm -rf %{buildroot}
%if %{with_ldpd}
%{_sbindir}/ldpd
%endif
+%if %{with_nhrpd}
+%{_sbindir}/nhrpd
+%endif
%if %{with_shared}
%attr(755,root,root) %{_libdir}/lib*.so
%attr(755,root,root) %{_libdir}/lib*.so.*
@@ -544,6 +562,9 @@ rm -rf %{buildroot}
%if %{with_ldpd}
%config /etc/rc.d/init.d/ldpd
%endif
+ %if %{with_nhrpd}
+ %config /etc/rc.d/init.d/nhrpd
+ %endif
%endif
%config(noreplace) /etc/default/frr
%config(noreplace) /etc/pam.d/frr
@@ -570,7 +591,10 @@ rm -rf %{buildroot}
%endif
%changelog
-* Fri Jan 6 2017 Martin Winter <mwinter@opensourcerouting.org> - %{version}
+* Tue Feb 14 2017 Timo Teräs <timo.teras@iki.fi> - %{version}
+- add nhrpd
+
+* Fri Jan 6 2017 Martin Winter <mwinter@opensourcerouting.org>
- Renamed to frr for FreeRangeRouting fork of Quagga
* Thu Feb 11 2016 Paul Jakma <paul@jakma.org>
diff --git a/ripd/rip_interface.c b/ripd/rip_interface.c
index 772ba49f93..4c750faf4c 100644
--- a/ripd/rip_interface.c
+++ b/ripd/rip_interface.c
@@ -86,8 +86,7 @@ ipv4_multicast_join (int sock,
ifindex);
if (ret < 0)
- zlog (NULL, LOG_INFO, "can't setsockopt IP_ADD_MEMBERSHIP %s",
- safe_strerror (errno));
+ zlog_info("can't setsockopt IP_ADD_MEMBERSHIP %s", safe_strerror(errno));
return ret;
}
@@ -108,7 +107,7 @@ ipv4_multicast_leave (int sock,
ifindex);
if (ret < 0)
- zlog (NULL, LOG_INFO, "can't setsockopt IP_DROP_MEMBERSHIP");
+ zlog_info("can't setsockopt IP_DROP_MEMBERSHIP");
return ret;
}
diff --git a/ripd/rip_main.c b/ripd/rip_main.c
index 58dd2df6fa..62ea6dd078 100644
--- a/ripd/rip_main.c
+++ b/ripd/rip_main.c
@@ -35,27 +35,14 @@
#include "sigevent.h"
#include "zclient.h"
#include "vrf.h"
-#include "sockopt.h"
+#include "libfrr.h"
#include "ripd/ripd.h"
/* ripd options. */
-#define OPTION_VTYSOCK 1000
static struct option longopts[] =
{
- { "daemon", no_argument, NULL, 'd'},
- { "config_file", required_argument, NULL, 'f'},
- { "pid_file", required_argument, NULL, 'i'},
- { "socket", required_argument, NULL, 'z'},
- { "help", no_argument, NULL, 'h'},
- { "dryrun", no_argument, NULL, 'C'},
- { "vty_addr", required_argument, NULL, 'A'},
- { "vty_port", required_argument, NULL, 'P'},
- { "vty_socket", required_argument, NULL, OPTION_VTYSOCK},
{ "retain", no_argument, NULL, 'r'},
- { "user", required_argument, NULL, 'u'},
- { "group", required_argument, NULL, 'g'},
- { "version", no_argument, NULL, 'v'},
{ 0 }
};
@@ -82,59 +69,13 @@ struct zebra_privs_t ripd_privs =
.cap_num_i = 0
};
-/* Configuration file and directory. */
-char config_default[] = SYSCONFDIR RIPD_DEFAULT_CONFIG;
-char *config_file = NULL;
-
-/* ripd program name */
-
-/* VTY Socket prefix */
-char vty_sock_path[MAXPATHLEN] = RIP_VTYSH_PATH;
-
/* Route retain mode flag. */
int retain_mode = 0;
-/* RIP VTY bind address. */
-char *vty_addr = NULL;
-
-/* RIP VTY connection port. */
-int vty_port = RIP_VTY_PORT;
-
/* Master of threads. */
struct thread_master *master;
-/* Process ID saved for use by init system */
-const char *pid_file = PATH_RIPD_PID;
-
-/* Help information display. */
-static void
-usage (char *progname, int status)
-{
- if (status != 0)
- fprintf (stderr, "Try `%s --help' for more information.\n", progname);
- else
- {
- printf ("Usage : %s [OPTION...]\n\
-Daemon which manages RIP version 1 and 2.\n\n\
--d, --daemon Runs in daemon mode\n\
--f, --config_file Set configuration file name\n\
--i, --pid_file Set process identifier file name\n\
--z, --socket Set path of zebra socket\n\
--A, --vty_addr Set vty's bind address\n\
--P, --vty_port Set vty's port number\n\
- --vty_socket Override vty socket path\n\
--C, --dryrun Check configuration for validity and exit\n\
--r, --retain When program terminates, retain added route by ripd.\n\
--u, --user User to run as\n\
--g, --group Group to run as\n\
--v, --version Print program version\n\
--h, --help Display this help and exit\n\
-\n\
-Report bugs to %s\n", progname, FRR_BUG_ADDRESS);
- }
-
- exit (status);
-}
+static struct frr_daemon_info ripd_di;
/* SIGHUP handler. */
static void
@@ -146,10 +87,7 @@ sighup (void)
zlog_info ("ripd restarting!");
/* Reload config file. */
- vty_read_config (config_file, config_default);
-
- /* Create VTY's socket */
- vty_serv_sock (vty_addr, vty_port, vty_sock_path);
+ vty_read_config (ripd_di.config_file, config_default);
/* Try to return to normal operation. */
}
@@ -170,7 +108,7 @@ sigint (void)
static void
sigusr1 (void)
{
- zlog_rotate (NULL);
+ zlog_rotate();
}
static struct quagga_signal_t ripd_signals[] =
@@ -193,36 +131,31 @@ static struct quagga_signal_t ripd_signals[] =
},
};
+FRR_DAEMON_INFO(ripd, RIP,
+ .vty_port = RIP_VTY_PORT,
+
+ .proghelp = "Implementation of the RIP routing protocol.",
+
+ .signals = ripd_signals,
+ .n_signals = array_size(ripd_signals),
+
+ .privs = &ripd_privs,
+)
+
/* Main routine of ripd. */
int
main (int argc, char **argv)
{
- char *p;
- int daemon_mode = 0;
- int dryrun = 0;
- char *progname;
- struct thread thread;
-
- /* Set umask before anything for security */
- umask (0027);
-
- /* Get program name. */
- progname = ((p = strrchr (argv[0], '/')) ? ++p : argv[0]);
-
- /* First of all we need logging init. */
- zlog_default = openzlog (progname, ZLOG_RIP, 0,
- LOG_CONS|LOG_NDELAY|LOG_PID, LOG_DAEMON);
- zprivs_init (&ripd_privs);
-#if defined(HAVE_CUMULUS)
- zlog_set_level (NULL, ZLOG_DEST_SYSLOG, zlog_default->default_lvl);
-#endif
+ frr_preinit (&ripd_di, argc, argv);
+ frr_opt_add ("r", longopts,
+ " -r, --retain When program terminates, retain added route by ripd.\n");
/* Command line option parse. */
while (1)
{
int opt;
- opt = getopt_long (argc, argv, "df:i:z:hA:P:u:g:rvC", longopts, 0);
+ opt = frr_getopt (argc, argv, NULL);
if (opt == EOF)
break;
@@ -231,69 +164,19 @@ main (int argc, char **argv)
{
case 0:
break;
- case 'd':
- daemon_mode = 1;
- break;
- case 'f':
- config_file = optarg;
- break;
- case 'A':
- vty_addr = optarg;
- break;
- case 'i':
- pid_file = optarg;
- break;
- case 'z':
- zclient_serv_path_set (optarg);
- break;
- case 'P':
- /* Deal with atoi() returning 0 on failure, and ripd not
- listening on rip port... */
- if (strcmp(optarg, "0") == 0)
- {
- vty_port = 0;
- break;
- }
- vty_port = atoi (optarg);
- if (vty_port <= 0 || vty_port > 0xffff)
- vty_port = RIP_VTY_PORT;
- break;
- case OPTION_VTYSOCK:
- set_socket_path(vty_sock_path, RIP_VTYSH_PATH, optarg, sizeof (vty_sock_path));
- break;
case 'r':
retain_mode = 1;
break;
- case 'C':
- dryrun = 1;
- break;
- case 'u':
- ripd_privs.user = optarg;
- break;
- case 'g':
- ripd_privs.group = optarg;
- break;
- case 'v':
- print_version (progname);
- exit (0);
- break;
- case 'h':
- usage (progname, 0);
- break;
default:
- usage (progname, 1);
+ frr_help_exit (1);
break;
}
}
/* Prepare master thread. */
- master = thread_master_create ();
+ master = frr_init ();
/* Library initialization. */
- signal_init (master, array_size(ripd_signals), ripd_signals);
- cmd_init (1);
- vty_init (master);
- memory_init ();
keychain_init ();
vrf_init ();
@@ -303,32 +186,8 @@ main (int argc, char **argv)
rip_zclient_init(master);
rip_peer_init ();
- /* Get configuration file. */
- vty_read_config (config_file, config_default);
-
- /* Start execution only if not in dry-run mode */
- if(dryrun)
- return (0);
-
- /* Change to the daemon program. */
- if (daemon_mode && daemon (0, 0) < 0)
- {
- zlog_err("RIPd daemon failed: %s", strerror(errno));
- exit (1);
- }
-
- /* Pid file create. */
- pid_output (pid_file);
-
- /* Create VTY's socket */
- vty_serv_sock (vty_addr, vty_port, vty_sock_path);
-
- /* Print banner. */
- zlog_notice ("RIPd %s starting: vty@%d", FRR_VERSION, vty_port);
-
- /* Execute each thread. */
- while (thread_fetch (master, &thread))
- thread_call (&thread);
+ frr_config_fork ();
+ frr_run (master);
/* Not reached. */
return (0);
diff --git a/ripd/ripd.c b/ripd/ripd.c
index bcf4cd3347..e0f96f9aaf 100644
--- a/ripd/ripd.c
+++ b/ripd/ripd.c
@@ -4034,9 +4034,6 @@ rip_routemap_update (const char *notused)
void
rip_init (void)
{
- /* Randomize for triggered update random(). */
- srandom (time (NULL));
-
/* Install top nodes. */
install_node (&rip_node, config_write_rip);
diff --git a/ripngd/ripng_main.c b/ripngd/ripng_main.c
index a90dffce14..9d97df5c41 100644
--- a/ripngd/ripng_main.c
+++ b/ripngd/ripng_main.c
@@ -36,31 +36,14 @@
#include "privs.h"
#include "sigevent.h"
#include "vrf.h"
-#include "sockopt.h"
+#include "libfrr.h"
#include "ripngd/ripngd.h"
-/* Configuration filename and directory. */
-char config_default[] = SYSCONFDIR RIPNG_DEFAULT_CONFIG;
-char *config_file = NULL;
-
/* RIPngd options. */
-#define OPTION_VTYSOCK 1000
struct option longopts[] =
{
- { "daemon", no_argument, NULL, 'd'},
- { "config_file", required_argument, NULL, 'f'},
- { "pid_file", required_argument, NULL, 'i'},
- { "socket", required_argument, NULL, 'z'},
- { "dryrun", no_argument, NULL, 'C'},
- { "help", no_argument, NULL, 'h'},
- { "vty_addr", required_argument, NULL, 'A'},
- { "vty_port", required_argument, NULL, 'P'},
- { "vty_socket", required_argument, NULL, OPTION_VTYSOCK},
{ "retain", no_argument, NULL, 'r'},
- { "user", required_argument, NULL, 'u'},
- { "group", required_argument, NULL, 'g'},
- { "version", no_argument, NULL, 'v'},
{ 0 }
};
@@ -90,52 +73,13 @@ struct zebra_privs_t ripngd_privs =
/* RIPngd program name */
-/* VTY Socket prefix */
-char vty_sock_path[MAXPATHLEN] = RIPNG_VTYSH_PATH;
-
/* Route retain mode flag. */
int retain_mode = 0;
-/* RIPng VTY bind address. */
-char *vty_addr = NULL;
-
-/* RIPng VTY connection port. */
-int vty_port = RIPNG_VTY_PORT;
-
/* Master of threads. */
struct thread_master *master;
-/* Process ID saved for use by init system */
-const char *pid_file = PATH_RIPNGD_PID;
-
-/* Help information display. */
-static void
-usage (char *progname, int status)
-{
- if (status != 0)
- fprintf (stderr, "Try `%s --help' for more information.\n", progname);
- else
- {
- printf ("Usage : %s [OPTION...]\n\
-Daemon which manages RIPng.\n\n\
--d, --daemon Runs in daemon mode\n\
--f, --config_file Set configuration file name\n\
--i, --pid_file Set process identifier file name\n\
--z, --socket Set path of zebra socket\n\
--A, --vty_addr Set vty's bind address\n\
--P, --vty_port Set vty's port number\n\
- --vty_socket Override vty socket path\n\
--r, --retain When program terminates, retain added route by ripngd.\n\
--u, --user User to run as\n\
--g, --group Group to run as\n\
--v, --version Print program version\n\
--C, --dryrun Check configuration for validity and exit\n\
--h, --help Display this help and exit\n\
-\n\
-Report bugs to %s\n", progname, FRR_BUG_ADDRESS);
- }
- exit (status);
-}
+static struct frr_daemon_info ripngd_di;
/* SIGHUP handler. */
static void
@@ -146,9 +90,7 @@ sighup (void)
ripng_reset ();
/* Reload config file. */
- vty_read_config (config_file, config_default);
- /* Create VTY's socket */
- vty_serv_sock (vty_addr, vty_port, vty_sock_path);
+ vty_read_config (ripngd_di.config_file, config_default);
/* Try to return to normal operation. */
}
@@ -169,7 +111,7 @@ sigint (void)
static void
sigusr1 (void)
{
- zlog_rotate (NULL);
+ zlog_rotate();
}
struct quagga_signal_t ripng_signals[] =
@@ -192,35 +134,30 @@ struct quagga_signal_t ripng_signals[] =
},
};
+FRR_DAEMON_INFO(ripngd, RIPNG,
+ .vty_port = RIPNG_VTY_PORT,
+
+ .proghelp = "Implementation of the RIPng routing protocol.",
+
+ .signals = ripng_signals,
+ .n_signals = array_size(ripng_signals),
+
+ .privs = &ripngd_privs,
+)
+
/* RIPngd main routine. */
int
main (int argc, char **argv)
{
- char *p;
- int vty_port = RIPNG_VTY_PORT;
- int daemon_mode = 0;
- char *progname;
- struct thread thread;
- int dryrun = 0;
-
- /* Set umask before anything for security */
- umask (0027);
-
- /* get program name */
- progname = ((p = strrchr (argv[0], '/')) ? ++p : argv[0]);
-
- zlog_default = openzlog(progname, ZLOG_RIPNG, 0,
- LOG_CONS|LOG_NDELAY|LOG_PID, LOG_DAEMON);
- zprivs_init (&ripngd_privs);
-#if defined(HAVE_CUMULUS)
- zlog_set_level (NULL, ZLOG_DEST_SYSLOG, zlog_default->default_lvl);
-#endif
+ frr_preinit (&ripngd_di, argc, argv);
+ frr_opt_add ("r", longopts,
+ " -r, --retain When program terminates, retain added route by ripd.\n");
while (1)
{
int opt;
- opt = getopt_long (argc, argv, "df:i:z:hA:P:u:g:vC", longopts, 0);
+ opt = frr_getopt (argc, argv, NULL);
if (opt == EOF)
break;
@@ -229,68 +166,18 @@ main (int argc, char **argv)
{
case 0:
break;
- case 'd':
- daemon_mode = 1;
- break;
- case 'f':
- config_file = optarg;
- break;
- case 'A':
- vty_addr = optarg;
- break;
- case 'i':
- pid_file = optarg;
- break;
- case 'z':
- zclient_serv_path_set (optarg);
- break;
- case 'P':
- /* Deal with atoi() returning 0 on failure, and ripngd not
- listening on ripngd port... */
- if (strcmp(optarg, "0") == 0)
- {
- vty_port = 0;
- break;
- }
- vty_port = atoi (optarg);
- if (vty_port <= 0 || vty_port > 0xffff)
- vty_port = RIPNG_VTY_PORT;
- break;
- case OPTION_VTYSOCK:
- set_socket_path(vty_sock_path, RIPNG_VTYSH_PATH, optarg, sizeof (vty_sock_path));
- break;
case 'r':
retain_mode = 1;
break;
- case 'u':
- ripngd_privs.user = optarg;
- break;
- case 'g':
- ripngd_privs.group = optarg;
- break;
- case 'v':
- print_version (progname);
- exit (0);
- break;
- case 'C':
- dryrun = 1;
- break;
- case 'h':
- usage (progname, 0);
- break;
default:
- usage (progname, 1);
+ frr_help_exit (1);
break;
}
}
- master = thread_master_create ();
+ master = frr_init ();
/* Library inits. */
- signal_init (master, array_size(ripng_signals), ripng_signals);
- cmd_init (1);
- vty_init (master);
- memory_init ();
vrf_init ();
/* RIPngd inits. */
@@ -298,32 +185,8 @@ main (int argc, char **argv)
zebra_init(master);
ripng_peer_init ();
- /* Get configuration file. */
- vty_read_config (config_file, config_default);
-
- /* Start execution only if not in dry-run mode */
- if(dryrun)
- return(0);
-
- /* Change to the daemon program. */
- if (daemon_mode && daemon (0, 0) < 0)
- {
- zlog_err("RIPNGd daemon failed: %s", strerror(errno));
- exit (1);
- }
-
- /* Create VTY socket */
- vty_serv_sock (vty_addr, vty_port, vty_sock_path);
-
- /* Process id file create. */
- pid_output (pid_file);
-
- /* Print banner. */
- zlog_notice ("RIPNGd %s starting: vty@%d", FRR_VERSION, vty_port);
-
- /* Fetch next active thread. */
- while (thread_fetch (master, &thread))
- thread_call (&thread);
+ frr_config_fork ();
+ frr_run (master);
/* Not reached. */
return 0;
diff --git a/ripngd/ripngd.c b/ripngd/ripngd.c
index c8c18928c9..108da21c11 100644
--- a/ripngd/ripngd.c
+++ b/ripngd/ripngd.c
@@ -107,7 +107,7 @@ ripng_make_socket (void)
sock = socket (AF_INET6, SOCK_DGRAM, 0);
if (sock < 0)
{
- zlog (NULL, LOG_ERR, "Can't make ripng socket");
+ zlog_err("Can't make ripng socket");
return sock;
}
@@ -143,7 +143,7 @@ ripng_make_socket (void)
ret = bind (sock, (struct sockaddr *) &ripaddr, sizeof (ripaddr));
if (ret < 0)
{
- zlog (NULL, LOG_ERR, "Can't bind ripng socket: %s.", safe_strerror (errno));
+ zlog_err("Can't bind ripng socket: %s.", safe_strerror(errno));
if (ripngd_privs.change (ZPRIVS_LOWER))
zlog_err ("ripng_make_socket: could not lower privs");
return ret;
@@ -1448,8 +1448,7 @@ ripng_update (struct thread *t)
if (ri->ri_send == RIPNG_SEND_OFF)
{
if (IS_RIPNG_DEBUG_EVENT)
- zlog (NULL, LOG_DEBUG,
- "[Event] RIPng send to if %d is suppressed by config",
+ zlog_debug ("[Event] RIPng send to if %d is suppressed by config",
ifp->ifindex);
continue;
}
@@ -3081,9 +3080,6 @@ ripng_routemap_update (const char *unused)
void
ripng_init ()
{
- /* Randomize. */
- srandom (time (NULL));
-
/* Install RIPNG_NODE. */
install_node (&cmd_ripng_node, ripng_config_write);
diff --git a/tests/bgpd/test_aspath.c b/tests/bgpd/test_aspath.c
index f3999cbcff..e5ff6a1377 100644
--- a/tests/bgpd/test_aspath.c
+++ b/tests/bgpd/test_aspath.c
@@ -1332,7 +1332,7 @@ main (void)
{
int i = 0;
qobj_init ();
- bgp_master_init ();
+ bgp_master_init (thread_master_create ());
master = bm->master;
bgp_option_set (BGP_OPT_NO_LISTEN);
bgp_attr_init ();
diff --git a/tests/bgpd/test_capability.c b/tests/bgpd/test_capability.c
index f83dee5e1c..c3de6a16e8 100644
--- a/tests/bgpd/test_capability.c
+++ b/tests/bgpd/test_capability.c
@@ -650,7 +650,7 @@ main (void)
qobj_init ();
master = thread_master_create ();
- bgp_master_init ();
+ bgp_master_init (master);
vrf_init ();
bgp_option_set (BGP_OPT_NO_LISTEN);
diff --git a/tests/bgpd/test_mp_attr.c b/tests/bgpd/test_mp_attr.c
index 883cac0cc6..6824c11fea 100644
--- a/tests/bgpd/test_mp_attr.c
+++ b/tests/bgpd/test_mp_attr.c
@@ -750,7 +750,7 @@ main (void)
qobj_init ();
master = thread_master_create ();
- bgp_master_init ();
+ bgp_master_init (master);
vrf_init ();
bgp_option_set (BGP_OPT_NO_LISTEN);
bgp_attr_init ();
diff --git a/tests/bgpd/test_mpath.c b/tests/bgpd/test_mpath.c
index f9eb1534f3..a2fd66528c 100644
--- a/tests/bgpd/test_mpath.c
+++ b/tests/bgpd/test_mpath.c
@@ -379,7 +379,7 @@ global_test_init (void)
qobj_init ();
master = thread_master_create ();
zclient = zclient_new(master);
- bgp_master_init ();
+ bgp_master_init (master);
vrf_init ();
bgp_option_set (BGP_OPT_NO_LISTEN);
diff --git a/tests/lib/cli/common_cli.c b/tests/lib/cli/common_cli.c
index 104352f516..56db460438 100644
--- a/tests/lib/cli/common_cli.c
+++ b/tests/lib/cli/common_cli.c
@@ -52,7 +52,7 @@ static void vty_do_exit(void)
cmd_terminate ();
vty_terminate ();
thread_master_free (master);
- closezlog (zlog_default);
+ closezlog ();
log_memstats_stderr ("testcli");
exit (0);
@@ -70,11 +70,11 @@ main (int argc, char **argv)
/* master init. */
master = thread_master_create ();
- zlog_default = openzlog ("common-cli", ZLOG_NONE, 0,
- LOG_CONS|LOG_NDELAY|LOG_PID, LOG_DAEMON);
- zlog_set_level (NULL, ZLOG_DEST_SYSLOG, ZLOG_DISABLED);
- zlog_set_level (NULL, ZLOG_DEST_STDOUT, ZLOG_DISABLED);
- zlog_set_level (NULL, ZLOG_DEST_MONITOR, LOG_DEBUG);
+ openzlog("common-cli", "NONE", 0, LOG_CONS | LOG_NDELAY | LOG_PID,
+ LOG_DAEMON);
+ zlog_set_level(ZLOG_DEST_SYSLOG, ZLOG_DISABLED);
+ zlog_set_level(ZLOG_DEST_STDOUT, ZLOG_DISABLED);
+ zlog_set_level(ZLOG_DEST_MONITOR, LOG_DEBUG);
/* Library inits. */
cmd_init (1);
diff --git a/tests/lib/test_segv.c b/tests/lib/test_segv.c
index 1810c5f4b2..dfc9d5f482 100644
--- a/tests/lib/test_segv.c
+++ b/tests/lib/test_segv.c
@@ -49,11 +49,10 @@ main (void)
master = thread_master_create ();
signal_init (master, array_size(sigs), sigs);
- zlog_default = openzlog("testsegv", ZLOG_NONE, 0,
- LOG_CONS|LOG_NDELAY|LOG_PID, LOG_DAEMON);
- zlog_set_level (NULL, ZLOG_DEST_SYSLOG, ZLOG_DISABLED);
- zlog_set_level (NULL, ZLOG_DEST_STDOUT, LOG_DEBUG);
- zlog_set_level (NULL, ZLOG_DEST_MONITOR, ZLOG_DISABLED);
+ openzlog("testsegv", "NONE", 0, LOG_CONS | LOG_NDELAY | LOG_PID, LOG_DAEMON);
+ zlog_set_level(ZLOG_DEST_SYSLOG, ZLOG_DISABLED);
+ zlog_set_level(ZLOG_DEST_STDOUT, LOG_DEBUG);
+ zlog_set_level(ZLOG_DEST_MONITOR, ZLOG_DISABLED);
thread_execute (master, threadfunc, 0, 0);
diff --git a/tests/lib/test_sig.c b/tests/lib/test_sig.c
index 4a04240303..10bce2303e 100644
--- a/tests/lib/test_sig.c
+++ b/tests/lib/test_sig.c
@@ -64,13 +64,12 @@ main (void)
{
master = thread_master_create ();
signal_init (master, array_size(sigs), sigs);
-
- zlog_default = openzlog("testsig", ZLOG_NONE, 0,
- LOG_CONS|LOG_NDELAY|LOG_PID, LOG_DAEMON);
- zlog_set_level (NULL, ZLOG_DEST_SYSLOG, ZLOG_DISABLED);
- zlog_set_level (NULL, ZLOG_DEST_STDOUT, LOG_DEBUG);
- zlog_set_level (NULL, ZLOG_DEST_MONITOR, ZLOG_DISABLED);
-
+
+ openzlog("testsig", "NONE", 0, LOG_CONS | LOG_NDELAY | LOG_PID, LOG_DAEMON);
+ zlog_set_level(ZLOG_DEST_SYSLOG, ZLOG_DISABLED);
+ zlog_set_level(ZLOG_DEST_STDOUT, LOG_DEBUG);
+ zlog_set_level(ZLOG_DEST_MONITOR, ZLOG_DISABLED);
+
while (thread_fetch (master, &t))
thread_call (&t);
diff --git a/tools/frr b/tools/frr
index 8524ab4083..80dd9e8747 100755
--- a/tools/frr
+++ b/tools/frr
@@ -21,7 +21,7 @@ V_PATH=/var/run/frr
# Local Daemon selection may be done by using /etc/frr/daemons.
# See /usr/share/doc/frr/README.Debian.gz for further information.
# Keep zebra first and do not list watchfrr!
-DAEMONS="zebra bgpd ripd ripngd ospfd ospf6d isisd babeld pimd ldpd"
+DAEMONS="zebra bgpd ripd ripngd ospfd ospf6d isisd babeld pimd ldpd nhrpd"
MAX_INSTANCES=5
RELOAD_SCRIPT=/usr/lib/frr/frr-reload.py
diff --git a/vtysh/Makefile.am b/vtysh/Makefile.am
index 376f380a32..d02ec9661f 100644
--- a/vtysh/Makefile.am
+++ b/vtysh/Makefile.am
@@ -67,6 +67,10 @@ if RIPNGD
vtysh_scan += $(top_srcdir)/ripngd/*.c
endif
+if NHRPD
+vtysh_scan += $(top_srcdir)/nhrpd/nhrp_vty.c
+endif
+
vtysh_cmd_FILES = $(vtysh_scan) \
$(top_srcdir)/lib/keychain.c $(top_srcdir)/lib/routemap.c \
$(top_srcdir)/lib/filter.c $(top_srcdir)/lib/plist.c \
diff --git a/vtysh/vtysh.c b/vtysh/vtysh.c
index 38f28e4530..114022d199 100644
--- a/vtysh/vtysh.c
+++ b/vtysh/vtysh.c
@@ -43,6 +43,7 @@
#include "bgpd/bgp_vty.h"
#include "ns.h"
#include "vrf.h"
+#include "libfrr.h"
DEFINE_MTYPE_STATIC(MVTYSH, VTYSH_CMD, "Vtysh cmd copy")
@@ -58,22 +59,23 @@ struct vtysh_client
int fd;
const char *name;
int flag;
- const char *path;
+ char path[MAXPATHLEN];
struct vtysh_client *next;
};
struct vtysh_client vtysh_client[] =
{
- { .fd = -1, .name = "zebra", .flag = VTYSH_ZEBRA, .path = ZEBRA_VTYSH_PATH, .next = NULL},
- { .fd = -1, .name = "ripd", .flag = VTYSH_RIPD, .path = RIP_VTYSH_PATH, .next = NULL},
- { .fd = -1, .name = "ripngd", .flag = VTYSH_RIPNGD, .path = RIPNG_VTYSH_PATH, .next = NULL},
- { .fd = -1, .name = "ospfd", .flag = VTYSH_OSPFD, .path = OSPF_VTYSH_PATH, .next = NULL},
- { .fd = -1, .name = "ospf6d", .flag = VTYSH_OSPF6D, .path = OSPF6_VTYSH_PATH, .next = NULL},
- { .fd = -1, .name = "ldpd", .flag = VTYSH_LDPD, .path = LDP_VTYSH_PATH, .next = NULL},
- { .fd = -1, .name = "bgpd", .flag = VTYSH_BGPD, .path = BGP_VTYSH_PATH, .next = NULL},
- { .fd = -1, .name = "isisd", .flag = VTYSH_ISISD, .path = ISIS_VTYSH_PATH, .next = NULL},
- { .fd = -1, .name = "pimd", .flag = VTYSH_PIMD, .path = PIM_VTYSH_PATH, .next = NULL},
- { .fd = -1, .name = "watchfrr", .flag = VTYSH_WATCHFRR, .path = WATCHFRR_VTYSH_PATH, .next = NULL},
+ { .fd = -1, .name = "zebra", .flag = VTYSH_ZEBRA, .next = NULL},
+ { .fd = -1, .name = "ripd", .flag = VTYSH_RIPD, .next = NULL},
+ { .fd = -1, .name = "ripngd", .flag = VTYSH_RIPNGD, .next = NULL},
+ { .fd = -1, .name = "ospfd", .flag = VTYSH_OSPFD, .next = NULL},
+ { .fd = -1, .name = "ospf6d", .flag = VTYSH_OSPF6D, .next = NULL},
+ { .fd = -1, .name = "ldpd", .flag = VTYSH_LDPD, .next = NULL},
+ { .fd = -1, .name = "bgpd", .flag = VTYSH_BGPD, .next = NULL},
+ { .fd = -1, .name = "isisd", .flag = VTYSH_ISISD, .next = NULL},
+ { .fd = -1, .name = "pimd", .flag = VTYSH_PIMD, .next = NULL},
+ { .fd = -1, .name = "nhrpd", .flag = VTYSH_NHRPD, .next = NULL},
+ { .fd = -1, .name = "watchfrr", .flag = VTYSH_WATCHFRR, .next = NULL},
};
enum vtysh_write_integrated vtysh_write_integrated = WRITE_INTEGRATED_UNSPECIFIED;
@@ -2865,27 +2867,12 @@ vtysh_connect (struct vtysh_client *vclient)
int sock, len;
struct sockaddr_un addr;
struct stat s_stat;
- char path[MAXPATHLEN];
+ const char *path;
- if (vty_sock_path == NULL)
- strlcpy (path, vclient->path, sizeof (path));
- else {
- /* Different path for VTY Socket specified
- overriding the default path, but keep the filename */
- strlcpy (path, vty_sock_path, sizeof (path));
-
- if (strrchr (vclient->path, '/') != NULL)
- strlcat (path, strrchr (vclient->path, '/'), sizeof (path));
- else {
- /*
- * vclient->path configured as relative path during config? Should
- * really never happen for sensible config
- */
- strlcat (path, "/", sizeof (path));
- strlcat (path, vclient->path, sizeof (path));
- }
- }
- path[sizeof(path)-1] = '\0';
+ if (!vclient->path[0])
+ snprintf(vclient->path, sizeof(vclient->path), "%s/%s.vty",
+ vty_sock_path, vclient->name);
+ path = vclient->path;
/* Stat socket to see if we have permission to access it. */
ret = stat (path, &s_stat);
@@ -2980,24 +2967,14 @@ static void
vtysh_update_all_insances(struct vtysh_client * head_client)
{
struct vtysh_client *client;
- char *ptr;
- char vty_dir[MAXPATHLEN];
DIR *dir;
struct dirent *file;
int n = 0;
if (head_client->flag != VTYSH_OSPFD) return;
- if (vty_sock_path == NULL)
- /* ls DAEMON_VTY_DIR and look for all files ending in .vty */
- strlcpy(vty_dir, DAEMON_VTY_DIR "/", MAXPATHLEN);
- else
- {
- /* ls vty_sock_dir and look for all files ending in .vty */
- strlcpy(vty_dir, vty_sock_path, MAXPATHLEN);
- strlcat(vty_dir, "/", MAXPATHLEN);
- }
- dir = opendir(vty_dir);
+ /* ls vty_sock_dir and look for all files ending in .vty */
+ dir = opendir(vty_sock_path);
if (dir)
{
while ((file = readdir(dir)) != NULL)
@@ -3008,16 +2985,15 @@ vtysh_update_all_insances(struct vtysh_client * head_client)
{
fprintf(stderr,
"Parsing %s, client limit(%d) reached!\n",
- vty_dir, n);
+ vty_sock_path, n);
break;
}
client = (struct vtysh_client *) malloc(sizeof(struct vtysh_client));
client->fd = -1;
client->name = "ospfd";
client->flag = VTYSH_OSPFD;
- ptr = (char *) malloc(100);
- sprintf(ptr, "%s%s", vty_dir, file->d_name);
- client->path = (const char *)ptr;
+ snprintf(client->path, sizeof(client->path), "%s/%s",
+ vty_sock_path, file->d_name);
client->next = NULL;
vtysh_client_sorted_insert(head_client, client);
n++;
diff --git a/vtysh/vtysh.h b/vtysh/vtysh.h
index ad31195ac4..07ba8367de 100644
--- a/vtysh/vtysh.h
+++ b/vtysh/vtysh.h
@@ -35,15 +35,16 @@ DECLARE_MGROUP(MVTYSH)
#define VTYSH_PIMD 0x100
#define VTYSH_LDPD 0x200
#define VTYSH_WATCHFRR 0x400
+#define VTYSH_NHRPD 0x800
/* commands in REALLYALL are crucial to correct vtysh operation */
#define VTYSH_REALLYALL ~0U
/* watchfrr is not in ALL since library CLI functions should not be
* run on it (logging & co. should stay in a fixed/frozen config, and
* things like prefix lists are not even initialised) */
-#define VTYSH_ALL VTYSH_ZEBRA|VTYSH_RIPD|VTYSH_RIPNGD|VTYSH_OSPFD|VTYSH_OSPF6D|VTYSH_LDPD|VTYSH_BGPD|VTYSH_ISISD|VTYSH_PIMD
+#define VTYSH_ALL VTYSH_ZEBRA|VTYSH_RIPD|VTYSH_RIPNGD|VTYSH_OSPFD|VTYSH_OSPF6D|VTYSH_LDPD|VTYSH_BGPD|VTYSH_ISISD|VTYSH_PIMD|VTYSH_NHRPD
#define VTYSH_RMAP VTYSH_ZEBRA|VTYSH_RIPD|VTYSH_RIPNGD|VTYSH_OSPFD|VTYSH_OSPF6D|VTYSH_BGPD|VTYSH_PIMD
-#define VTYSH_INTERFACE VTYSH_ZEBRA|VTYSH_RIPD|VTYSH_RIPNGD|VTYSH_OSPFD|VTYSH_OSPF6D|VTYSH_LDPD|VTYSH_ISISD|VTYSH_PIMD
+#define VTYSH_INTERFACE VTYSH_ZEBRA|VTYSH_RIPD|VTYSH_RIPNGD|VTYSH_OSPFD|VTYSH_OSPF6D|VTYSH_LDPD|VTYSH_ISISD|VTYSH_PIMD|VTYSH_NHRPD
#define VTYSH_NS VTYSH_ZEBRA
#define VTYSH_VRF VTYSH_ZEBRA
@@ -96,6 +97,6 @@ extern int execute_flag;
extern struct vty *vty;
-extern char * vty_sock_path;
+extern const char * vty_sock_path;
#endif /* VTYSH_H */
diff --git a/vtysh/vtysh_main.c b/vtysh/vtysh_main.c
index 3dffa05ace..bf62850e22 100644
--- a/vtysh/vtysh_main.c
+++ b/vtysh/vtysh_main.c
@@ -37,6 +37,7 @@
#include "memory.h"
#include "linklist.h"
#include "memory_vty.h"
+#include "libfrr.h"
#include "vtysh/vtysh.h"
#include "vtysh/vtysh_user.h"
@@ -54,7 +55,7 @@ char history_file[MAXPATHLEN];
int execute_flag = 0;
/* VTY Socket prefix */
-char * vty_sock_path = NULL;
+const char * vty_sock_path = NULL;
/* For sigsetjmp() & siglongjmp(). */
static sigjmp_buf jmpbuf;
@@ -402,6 +403,9 @@ main (int argc, char **argv, char **env)
}
}
+ if (!vty_sock_path)
+ vty_sock_path = frr_vtydir;
+
if (markfile + writeconfig + dryrun + boot_flag > 1)
{
fprintf (stderr, "Invalid combination of arguments. Please specify at "
diff --git a/watchfrr/watchfrr.c b/watchfrr/watchfrr.c
index f61dd3858b..413a631005 100644
--- a/watchfrr/watchfrr.c
+++ b/watchfrr/watchfrr.c
@@ -26,6 +26,7 @@
#include <lib/version.h>
#include "command.h"
#include "memory_vty.h"
+#include "libfrr.h"
#include <getopt.h>
#include <sys/un.h>
@@ -202,13 +203,10 @@ static int wakeup_send_echo(struct thread *t_wakeup);
static void try_restart(struct daemon *dmn);
static void phase_check(void);
-static int usage(const char *progname, int status)
+static const char *progname;
+static void printhelp(FILE *target)
{
- if (status != 0)
- fprintf(stderr, "Try `%s --help' for more information.\n",
- progname);
- else {
- printf("Usage : %s [OPTION...] <daemon name> ...\n\n\
+ fprintf(target, "Usage : %s [OPTION...] <daemon name> ...\n\n\
Watchdog program to monitor status of frr daemons and try to restart\n\
them if they are down or unresponsive. It determines whether a daemon is\n\
up based on whether it can connect to the daemon's vty unix stream socket.\n\
@@ -254,7 +252,7 @@ a restart is attempted: if the time since the last restart attempt exceeds\n\
twice the -M value, then the restart delay is set to the -m value.\n\
Otherwise, the interval is doubled (but capped at the -M value).\n\n", progname, mode_str[0], progname, mode_str[1], progname, mode_str[2], progname, mode_str[3], progname, mode_str[4], progname, mode_str[2], mode_str[3]);
- printf("Options:\n\
+ fprintf(target, "Options:\n\
-d, --daemon Run in daemon mode. In this mode, error messages are sent\n\
to syslog instead of stdout.\n\
-S, --statedir Set the vty socket directory (default is %s)\n\
@@ -313,9 +311,6 @@ Otherwise, the interval is doubled (but capped at the -M value).\n\n", progname,
passing command-line arguments with embedded spaces.\n\
-v, --version Print program version\n\
-h, --help Display this help and exit\n", VTYDIR, DEFAULT_LOGLEVEL, LOG_EMERG, LOG_DEBUG, LOG_DEBUG, DEFAULT_MIN_RESTART, DEFAULT_MAX_RESTART, DEFAULT_PERIOD, DEFAULT_TIMEOUT, DEFAULT_RESTART_TIMEOUT, DEFAULT_PIDFILE);
- }
-
- return status;
}
static pid_t run_background(char *shell_cmd)
@@ -1011,38 +1006,48 @@ struct zebra_privs_t watchfrr_privs = {
#endif
};
+static struct quagga_signal_t watchfrr_signals[] = {
+ {
+ .signal = SIGINT,
+ .handler = sigint,
+ },
+ {
+ .signal = SIGTERM,
+ .handler = sigint,
+ },
+ {
+ .signal = SIGCHLD,
+ .handler = sigchild,
+ },
+};
+
+FRR_DAEMON_INFO(watchfrr, WATCHFRR,
+ .flags = FRR_NO_PRIVSEP | FRR_NO_TCPVTY | FRR_LIMITED_CLI
+ | FRR_NO_CFG_PID_DRY | FRR_NO_ZCLIENT,
+
+ .printhelp = printhelp,
+ .copyright = "Copyright 2004 Andrew J. Schorr",
+
+ .signals = watchfrr_signals,
+ .n_signals = array_size(watchfrr_signals),
+
+ .privs = &watchfrr_privs,
+)
+
int main(int argc, char **argv)
{
- const char *progname;
int opt;
- int daemon_mode = 0;
const char *pidfile = DEFAULT_PIDFILE;
const char *special = "zebra";
const char *blankstr = NULL;
- static struct quagga_signal_t my_signals[] = {
- {
- .signal = SIGINT,
- .handler = sigint,
- },
- {
- .signal = SIGTERM,
- .handler = sigint,
- },
- {
- .signal = SIGCHLD,
- .handler = sigchild,
- },
- };
- if ((progname = strrchr(argv[0], '/')) != NULL)
- progname++;
- else
- progname = argv[0];
+ frr_preinit(&watchfrr_di, argc, argv);
+ progname = watchfrr_di.progname;
+
+ frr_opt_add("aAb:dek:l:m:M:i:p:r:R:S:s:t:T:z", longopts, "");
gs.restart.name = "all";
- while ((opt =
- getopt_long(argc, argv, "aAb:dek:l:m:M:i:p:r:R:S:s:t:T:zvh",
- longopts, 0)) != EOF) {
+ while ((opt = frr_getopt(argc, argv, NULL)) != EOF) {
switch (opt) {
case 0:
break;
@@ -1051,7 +1056,7 @@ int main(int argc, char **argv)
&& (gs.mode != MODE_SEPARATE_RESTART)) {
fputs("Ambiguous operating mode selected.\n",
stderr);
- return usage(progname, 1);
+ frr_help_exit(1);
}
gs.mode = MODE_PHASED_ZEBRA_RESTART;
break;
@@ -1060,16 +1065,13 @@ int main(int argc, char **argv)
&& (gs.mode != MODE_SEPARATE_RESTART)) {
fputs("Ambiguous operating mode selected.\n",
stderr);
- return usage(progname, 1);
+ frr_help_exit(1);
}
gs.mode = MODE_PHASED_ALL_RESTART;
break;
case 'b':
blankstr = optarg;
break;
- case 'd':
- daemon_mode = 1;
- break;
case 'e':
gs.do_ping = 0;
break;
@@ -1078,7 +1080,7 @@ int main(int argc, char **argv)
fprintf(stderr,
"Invalid kill command, must contain '%%s': %s\n",
optarg);
- return usage(progname, 1);
+ frr_help_exit(1);
}
gs.stop_command = optarg;
break;
@@ -1092,7 +1094,7 @@ int main(int argc, char **argv)
fprintf(stderr,
"Invalid loglevel argument: %s\n",
optarg);
- return usage(progname, 1);
+ frr_help_exit(1);
}
}
break;
@@ -1106,7 +1108,7 @@ int main(int argc, char **argv)
fprintf(stderr,
"Invalid min_restart_interval argument: %s\n",
optarg);
- return usage(progname, 1);
+ frr_help_exit(1);
}
}
break;
@@ -1120,7 +1122,7 @@ int main(int argc, char **argv)
fprintf(stderr,
"Invalid max_restart_interval argument: %s\n",
optarg);
- return usage(progname, 1);
+ frr_help_exit(1);
}
}
break;
@@ -1133,7 +1135,7 @@ int main(int argc, char **argv)
fprintf(stderr,
"Invalid interval argument: %s\n",
optarg);
- return usage(progname, 1);
+ frr_help_exit(1);
}
gs.period = 1000 * period;
}
@@ -1146,13 +1148,13 @@ int main(int argc, char **argv)
(gs.mode == MODE_SEPARATE_RESTART)) {
fputs("Ambiguous operating mode selected.\n",
stderr);
- return usage(progname, 1);
+ frr_help_exit(1);
}
if (!valid_command(optarg)) {
fprintf(stderr,
"Invalid restart command, must contain '%%s': %s\n",
optarg);
- return usage(progname, 1);
+ frr_help_exit(1);
}
gs.restart_command = optarg;
if (gs.mode == MODE_MONITOR)
@@ -1162,13 +1164,13 @@ int main(int argc, char **argv)
if (gs.mode != MODE_MONITOR) {
fputs("Ambiguous operating mode selected.\n",
stderr);
- return usage(progname, 1);
+ frr_help_exit(1);
}
if (strchr(optarg, '%')) {
fprintf(stderr,
"Invalid restart-all arg, must not contain '%%s': %s\n",
optarg);
- return usage(progname, 1);
+ frr_help_exit(1);
}
gs.restart_command = optarg;
gs.mode = MODE_GLOBAL_RESTART;
@@ -1178,7 +1180,7 @@ int main(int argc, char **argv)
fprintf(stderr,
"Invalid start command, must contain '%%s': %s\n",
optarg);
- return usage(progname, 1);
+ frr_help_exit(1);
}
gs.start_command = optarg;
break;
@@ -1194,7 +1196,7 @@ int main(int argc, char **argv)
fprintf(stderr,
"Invalid timeout argument: %s\n",
optarg);
- return usage(progname, 1);
+ frr_help_exit(1);
}
}
break;
@@ -1208,29 +1210,23 @@ int main(int argc, char **argv)
fprintf(stderr,
"Invalid restart timeout argument: %s\n",
optarg);
- return usage(progname, 1);
+ frr_help_exit(1);
}
}
break;
case 'z':
gs.unresponsive_restart = 1;
break;
- case 'v':
- printf("%s version %s\n", progname, FRR_VERSION);
- puts("Copyright 2004 Andrew J. Schorr");
- return 0;
- case 'h':
- return usage(progname, 0);
default:
fputs("Invalid option.\n", stderr);
- return usage(progname, 1);
+ frr_help_exit(1);
}
}
if (gs.unresponsive_restart && (gs.mode == MODE_MONITOR)) {
fputs("Option -z requires a -r or -R restart option.\n",
stderr);
- return usage(progname, 1);
+ frr_help_exit(1);
}
switch (gs.mode) {
case MODE_MONITOR:
@@ -1238,7 +1234,7 @@ int main(int argc, char **argv)
fprintf(stderr,
"No kill/(re)start commands needed for %s mode.\n",
mode_str[gs.mode]);
- return usage(progname, 1);
+ frr_help_exit(1);
}
break;
case MODE_GLOBAL_RESTART:
@@ -1247,7 +1243,7 @@ int main(int argc, char **argv)
fprintf(stderr,
"No start/kill commands needed in [%s] mode.\n",
mode_str[gs.mode]);
- return usage(progname, 1);
+ frr_help_exit(1);
}
break;
case MODE_PHASED_ZEBRA_RESTART:
@@ -1257,7 +1253,7 @@ int main(int argc, char **argv)
fprintf(stderr,
"Need start, kill, and restart commands in [%s] mode.\n",
mode_str[gs.mode]);
- return usage(progname, 1);
+ frr_help_exit(1);
}
break;
}
@@ -1276,17 +1272,22 @@ int main(int argc, char **argv)
gs.restart.interval = gs.min_restart_interval;
- zprivs_init(&watchfrr_privs);
+ master = frr_init();
+
+ zlog_set_level(ZLOG_DEST_MONITOR, ZLOG_DISABLED);
+ if (watchfrr_di.daemon_mode) {
+ zlog_set_level(ZLOG_DEST_SYSLOG, MIN(gs.loglevel, LOG_DEBUG));
+ if (daemon (0, 0) < 0) {
+ fprintf(stderr, "Watchquagga daemon failed: %s",
+ strerror(errno));
+ exit (1);
+ }
+ } else
+ zlog_set_level(ZLOG_DEST_STDOUT, MIN(gs.loglevel, LOG_DEBUG));
- master = thread_master_create();
- cmd_init(-1);
- memory_init();
- vty_init(master);
watchfrr_vty_init();
- vty_serv_sock(NULL, 0, WATCHFRR_VTYSH_PATH);
- signal_init(master, array_size(my_signals), my_signals);
- srandom(time(NULL));
+ frr_vty_serv();
{
int i;
@@ -1324,31 +1325,16 @@ int main(int argc, char **argv)
}
if (!gs.daemons) {
fputs("Must specify one or more daemons to monitor.\n", stderr);
- return usage(progname, 1);
+ frr_help_exit(1);
}
if (((gs.mode == MODE_PHASED_ZEBRA_RESTART) ||
(gs.mode == MODE_PHASED_ALL_RESTART)) && !gs.special) {
fprintf(stderr,
"In mode [%s], but cannot find master daemon %s\n",
mode_str[gs.mode], special);
- return usage(progname, 1);
+ frr_help_exit(1);
}
- zlog_default = openzlog(progname, ZLOG_WATCHFRR, 0,
- LOG_CONS | LOG_NDELAY | LOG_PID, LOG_DAEMON);
- zlog_set_level(NULL, ZLOG_DEST_MONITOR, ZLOG_DISABLED);
- if (daemon_mode) {
- zlog_set_level(NULL, ZLOG_DEST_SYSLOG,
- MIN(gs.loglevel, LOG_DEBUG));
- if (daemon(0, 0) < 0) {
- fprintf(stderr, "Watchfrr daemon failed: %s",
- strerror(errno));
- exit(1);
- }
- } else
- zlog_set_level(NULL, ZLOG_DEST_STDOUT,
- MIN(gs.loglevel, LOG_DEBUG));
-
/* Make sure we're not already running. */
pid_output(pidfile);
diff --git a/zebra/client_main.c b/zebra/client_main.c
index f40d995466..178184d463 100644
--- a/zebra/client_main.c
+++ b/zebra/client_main.c
@@ -115,6 +115,7 @@ struct zebra_info
{ "ospf", ZEBRA_ROUTE_OSPF },
{ "ospf6", ZEBRA_ROUTE_OSPF6 },
{ "bgp", ZEBRA_ROUTE_BGP },
+ { "nhrp", ZEBRA_ROUTE_NHRP },
{ NULL, 0 }
};
diff --git a/zebra/if_ioctl_solaris.c b/zebra/if_ioctl_solaris.c
index f27dc89007..339e00b22c 100644
--- a/zebra/if_ioctl_solaris.c
+++ b/zebra/if_ioctl_solaris.c
@@ -59,7 +59,7 @@ interface_list_ioctl (int af)
char *buf = NULL;
if (zserv_privs.change(ZPRIVS_RAISE))
- zlog (NULL, LOG_ERR, "Can't raise privileges");
+ zlog_err("Can't raise privileges");
sock = socket (af, SOCK_DGRAM, 0);
if (sock < 0)
@@ -68,7 +68,7 @@ interface_list_ioctl (int af)
(af == AF_INET ? "AF_INET" : "AF_INET6"), safe_strerror (errno));
if (zserv_privs.change(ZPRIVS_LOWER))
- zlog (NULL, LOG_ERR, "Can't lower privileges");
+ zlog_err("Can't lower privileges");
return -1;
}
@@ -80,7 +80,7 @@ calculate_lifc_len: /* must hold privileges to enter here */
save_errno = errno;
if (zserv_privs.change(ZPRIVS_LOWER))
- zlog (NULL, LOG_ERR, "Can't lower privileges");
+ zlog_err("Can't lower privileges");
if (ret < 0)
{
@@ -117,7 +117,7 @@ calculate_lifc_len: /* must hold privileges to enter here */
lifconf.lifc_buf = buf;
if (zserv_privs.change(ZPRIVS_RAISE))
- zlog (NULL, LOG_ERR, "Can't raise privileges");
+ zlog_err("Can't raise privileges");
ret = ioctl (sock, SIOCGLIFCONF, &lifconf);
@@ -129,13 +129,13 @@ calculate_lifc_len: /* must hold privileges to enter here */
zlog_warn ("SIOCGLIFCONF: %s", safe_strerror (errno));
if (zserv_privs.change(ZPRIVS_LOWER))
- zlog (NULL, LOG_ERR, "Can't lower privileges");
+ zlog_err("Can't lower privileges");
goto end;
}
if (zserv_privs.change(ZPRIVS_LOWER))
- zlog (NULL, LOG_ERR, "Can't lower privileges");
+ zlog_err("Can't lower privileges");
/* Allocate interface. */
lifreq = lifconf.lifc_req;
diff --git a/zebra/if_sysctl.c b/zebra/if_sysctl.c
index 0b71c76214..8326f0e396 100644
--- a/zebra/if_sysctl.c
+++ b/zebra/if_sysctl.c
@@ -69,7 +69,7 @@ ifstat_update_sysctl (void)
/* Fetch interface informations into allocated buffer. */
if (sysctl (mib, MIBSIZ, buf, &bufsiz, NULL, 0) < 0)
{
- zlog (NULL, LOG_WARNING, "sysctl error by %s", safe_strerror (errno));
+ zlog_warn("sysctl error by %s", safe_strerror(errno));
XFREE(MTYPE_TMP, ref);
return;
}
@@ -120,7 +120,7 @@ interface_list (struct zebra_ns *zns)
/* Query buffer size. */
if (sysctl (mib, MIBSIZ, NULL, &bufsiz, NULL, 0) < 0)
{
- zlog (NULL, LOG_WARNING, "sysctl() error by %s", safe_strerror (errno));
+ zlog_warn("sysctl() error by %s", safe_strerror(errno));
return;
}
@@ -130,7 +130,7 @@ interface_list (struct zebra_ns *zns)
/* Fetch interface informations into allocated buffer. */
if (sysctl (mib, MIBSIZ, buf, &bufsiz, NULL, 0) < 0)
{
- zlog (NULL, LOG_WARNING, "sysctl error by %s", safe_strerror (errno));
+ zlog_warn("sysctl error by %s", safe_strerror(errno));
return;
}
diff --git a/zebra/ioctl.c b/zebra/ioctl.c
index 1125043310..dfd69300f9 100644
--- a/zebra/ioctl.c
+++ b/zebra/ioctl.c
@@ -56,20 +56,20 @@ if_ioctl (u_long request, caddr_t buffer)
int err = 0;
if (zserv_privs.change(ZPRIVS_RAISE))
- zlog (NULL, LOG_ERR, "Can't raise privileges");
+ zlog_err("Can't raise privileges");
sock = socket (AF_INET, SOCK_DGRAM, 0);
if (sock < 0)
{
int save_errno = errno;
if (zserv_privs.change(ZPRIVS_LOWER))
- zlog (NULL, LOG_ERR, "Can't lower privileges");
+ zlog_err("Can't lower privileges");
zlog_err("Cannot create UDP socket: %s", safe_strerror(save_errno));
exit (1);
}
if ((ret = ioctl (sock, request, buffer)) < 0)
err = errno;
if (zserv_privs.change(ZPRIVS_LOWER))
- zlog (NULL, LOG_ERR, "Can't lower privileges");
+ zlog_err("Can't lower privileges");
close (sock);
if (ret < 0)
@@ -88,13 +88,13 @@ if_ioctl_ipv6 (u_long request, caddr_t buffer)
int err = 0;
if (zserv_privs.change(ZPRIVS_RAISE))
- zlog (NULL, LOG_ERR, "Can't raise privileges");
+ zlog_err("Can't raise privileges");
sock = socket (AF_INET6, SOCK_DGRAM, 0);
if (sock < 0)
{
int save_errno = errno;
if (zserv_privs.change(ZPRIVS_LOWER))
- zlog (NULL, LOG_ERR, "Can't lower privileges");
+ zlog_err("Can't lower privileges");
zlog_err("Cannot create IPv6 datagram socket: %s",
safe_strerror(save_errno));
exit (1);
@@ -103,7 +103,7 @@ if_ioctl_ipv6 (u_long request, caddr_t buffer)
if ((ret = ioctl (sock, request, buffer)) < 0)
err = errno;
if (zserv_privs.change(ZPRIVS_LOWER))
- zlog (NULL, LOG_ERR, "Can't lower privileges");
+ zlog_err("Can't lower privileges");
close (sock);
if (ret < 0)
@@ -162,7 +162,7 @@ if_get_mtu (struct interface *ifp)
zebra_interface_up_update(ifp);
#else
- zlog (NULL, LOG_INFO, "Can't lookup mtu on this system");
+ zlog_info("Can't lookup mtu on this system");
ifp->mtu6 = ifp->mtu = -1;
#endif
}
diff --git a/zebra/ioctl_solaris.c b/zebra/ioctl_solaris.c
index 1de583577d..78796a8a28 100644
--- a/zebra/ioctl_solaris.c
+++ b/zebra/ioctl_solaris.c
@@ -53,14 +53,14 @@ if_ioctl (u_long request, caddr_t buffer)
int err;
if (zserv_privs.change(ZPRIVS_RAISE))
- zlog (NULL, LOG_ERR, "Can't raise privileges");
+ zlog_err("Can't raise privileges");
sock = socket (AF_INET, SOCK_DGRAM, 0);
if (sock < 0)
{
int save_errno = errno;
if (zserv_privs.change(ZPRIVS_LOWER))
- zlog (NULL, LOG_ERR, "Can't lower privileges");
+ zlog_err("Can't lower privileges");
zlog_err("Cannot create UDP socket: %s", safe_strerror(save_errno));
exit (1);
}
@@ -69,7 +69,7 @@ if_ioctl (u_long request, caddr_t buffer)
err = errno;
if (zserv_privs.change(ZPRIVS_LOWER))
- zlog (NULL, LOG_ERR, "Can't lower privileges");
+ zlog_err("Can't lower privileges");
close (sock);
@@ -90,14 +90,14 @@ if_ioctl_ipv6 (u_long request, caddr_t buffer)
int err;
if (zserv_privs.change(ZPRIVS_RAISE))
- zlog (NULL, LOG_ERR, "Can't raise privileges");
+ zlog_err("Can't raise privileges");
sock = socket (AF_INET6, SOCK_DGRAM, 0);
if (sock < 0)
{
int save_errno = errno;
if (zserv_privs.change(ZPRIVS_LOWER))
- zlog (NULL, LOG_ERR, "Can't lower privileges");
+ zlog_err("Can't lower privileges");
zlog_err("Cannot create IPv6 datagram socket: %s",
safe_strerror(save_errno));
exit (1);
@@ -107,7 +107,7 @@ if_ioctl_ipv6 (u_long request, caddr_t buffer)
err = errno;
if (zserv_privs.change(ZPRIVS_LOWER))
- zlog (NULL, LOG_ERR, "Can't lower privileges");
+ zlog_err("Can't lower privileges");
close (sock);
diff --git a/zebra/ipforward_sysctl.c b/zebra/ipforward_sysctl.c
index be4c9cd99c..28894f4e0a 100644
--- a/zebra/ipforward_sysctl.c
+++ b/zebra/ipforward_sysctl.c
@@ -61,16 +61,16 @@ ipforward_on (void)
len = sizeof ipforwarding;
if (zserv_privs.change(ZPRIVS_RAISE))
- zlog (NULL, LOG_ERR, "Can't raise privileges");
+ zlog_err("Can't raise privileges");
if (sysctl (mib, MIB_SIZ, NULL, NULL, &ipforwarding, len) < 0)
{
if (zserv_privs.change(ZPRIVS_LOWER))
- zlog (NULL, LOG_ERR, "Can't lower privileges");
+ zlog_err("Can't lower privileges");
zlog_warn ("Can't set ipforwarding on");
return -1;
}
if (zserv_privs.change(ZPRIVS_LOWER))
- zlog (NULL, LOG_ERR, "Can't lower privileges");
+ zlog_err("Can't lower privileges");
return ipforwarding;
}
@@ -82,16 +82,16 @@ ipforward_off (void)
len = sizeof ipforwarding;
if (zserv_privs.change(ZPRIVS_RAISE))
- zlog (NULL, LOG_ERR, "Can't raise privileges");
+ zlog_err("Can't raise privileges");
if (sysctl (mib, MIB_SIZ, NULL, NULL, &ipforwarding, len) < 0)
{
if (zserv_privs.change(ZPRIVS_LOWER))
- zlog (NULL, LOG_ERR, "Can't lower privileges");
+ zlog_err("Can't lower privileges");
zlog_warn ("Can't set ipforwarding on");
return -1;
}
if (zserv_privs.change(ZPRIVS_LOWER))
- zlog (NULL, LOG_ERR, "Can't lower privileges");
+ zlog_err("Can't lower privileges");
return ipforwarding;
}
@@ -117,16 +117,16 @@ ipforward_ipv6 (void)
len = sizeof ip6forwarding;
if (zserv_privs.change(ZPRIVS_RAISE))
- zlog (NULL, LOG_ERR, "Can't raise privileges");
+ zlog_err("Can't raise privileges");
if (sysctl (mib_ipv6, MIB_SIZ, &ip6forwarding, &len, 0, 0) < 0)
{
if (zserv_privs.change(ZPRIVS_LOWER))
- zlog (NULL, LOG_ERR, "Can't lower privileges");
+ zlog_err("Can't lower privileges");
zlog_warn ("can't get ip6forwarding value");
return -1;
}
if (zserv_privs.change(ZPRIVS_LOWER))
- zlog (NULL, LOG_ERR, "Can't lower privileges");
+ zlog_err("Can't lower privileges");
return ip6forwarding;
}
@@ -138,16 +138,16 @@ ipforward_ipv6_on (void)
len = sizeof ip6forwarding;
if (zserv_privs.change(ZPRIVS_RAISE))
- zlog (NULL, LOG_ERR, "Can't raise privileges");
+ zlog_err("Can't raise privileges");
if (sysctl (mib_ipv6, MIB_SIZ, NULL, NULL, &ip6forwarding, len) < 0)
{
if (zserv_privs.change(ZPRIVS_LOWER))
- zlog (NULL, LOG_ERR, "Can't lower privileges");
+ zlog_err("Can't lower privileges");
zlog_warn ("can't get ip6forwarding value");
return -1;
}
if (zserv_privs.change(ZPRIVS_LOWER))
- zlog (NULL, LOG_ERR, "Can't lower privileges");
+ zlog_err("Can't lower privileges");
return ip6forwarding;
}
@@ -159,15 +159,15 @@ ipforward_ipv6_off (void)
len = sizeof ip6forwarding;
if (zserv_privs.change(ZPRIVS_RAISE))
- zlog (NULL, LOG_ERR, "Can't raise privileges");
+ zlog_err("Can't raise privileges");
if (sysctl (mib_ipv6, MIB_SIZ, NULL, NULL, &ip6forwarding, len) < 0)
{
if (zserv_privs.change(ZPRIVS_LOWER))
- zlog (NULL, LOG_ERR, "Can't lower privileges");
+ zlog_err("Can't lower privileges");
zlog_warn ("can't get ip6forwarding value");
return -1;
}
if (zserv_privs.change(ZPRIVS_LOWER))
- zlog (NULL, LOG_ERR, "Can't lower privileges");
+ zlog_err("Can't lower privileges");
return ip6forwarding;
}
diff --git a/zebra/kernel_netlink.c b/zebra/kernel_netlink.c
index c9c2d90eac..49394bd6f8 100644
--- a/zebra/kernel_netlink.c
+++ b/zebra/kernel_netlink.c
@@ -143,8 +143,8 @@ netlink_recvbuf (struct nlsock *nl, uint32_t newsize)
ret = getsockopt(nl->sock, SOL_SOCKET, SO_RCVBUF, &oldsize, &oldlen);
if (ret < 0)
{
- zlog (NULL, LOG_ERR, "Can't get %s receive buffer size: %s", nl->name,
- safe_strerror (errno));
+ zlog_err("Can't get %s receive buffer size: %s", nl->name,
+ safe_strerror(errno));
return -1;
}
@@ -160,22 +160,21 @@ netlink_recvbuf (struct nlsock *nl, uint32_t newsize)
sizeof(nl_rcvbufsize));
if (ret < 0)
{
- zlog (NULL, LOG_ERR, "Can't set %s receive buffer size: %s", nl->name,
- safe_strerror (errno));
+ zlog_err("Can't set %s receive buffer size: %s", nl->name,
+ safe_strerror(errno));
return -1;
}
ret = getsockopt(nl->sock, SOL_SOCKET, SO_RCVBUF, &newsize, &newlen);
if (ret < 0)
{
- zlog (NULL, LOG_ERR, "Can't get %s receive buffer size: %s", nl->name,
- safe_strerror (errno));
+ zlog_err("Can't get %s receive buffer size: %s", nl->name,
+ safe_strerror(errno));
return -1;
}
- zlog (NULL, LOG_INFO,
- "Setting netlink socket receive buffer size: %u -> %u",
- oldsize, newsize);
+ zlog_info("Setting netlink socket receive buffer size: %u -> %u", oldsize,
+ newsize);
return 0;
}
@@ -191,15 +190,14 @@ netlink_socket (struct nlsock *nl, unsigned long groups, ns_id_t ns_id)
if (zserv_privs.change (ZPRIVS_RAISE))
{
- zlog (NULL, LOG_ERR, "Can't raise privileges");
+ zlog_err("Can't raise privileges");
return -1;
}
sock = socket (AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
if (sock < 0)
{
- zlog (NULL, LOG_ERR, "Can't open %s socket: %s", nl->name,
- safe_strerror (errno));
+ zlog_err("Can't open %s socket: %s", nl->name, safe_strerror(errno));
return -1;
}
@@ -211,12 +209,12 @@ netlink_socket (struct nlsock *nl, unsigned long groups, ns_id_t ns_id)
ret = bind (sock, (struct sockaddr *) &snl, sizeof snl);
save_errno = errno;
if (zserv_privs.change (ZPRIVS_LOWER))
- zlog (NULL, LOG_ERR, "Can't lower privileges");
+ zlog_err("Can't lower privileges");
if (ret < 0)
{
- zlog (NULL, LOG_ERR, "Can't bind %s socket to group 0x%x: %s",
- nl->name, snl.nl_groups, safe_strerror (save_errno));
+ zlog_err("Can't bind %s socket to group 0x%x: %s", nl->name,
+ snl.nl_groups, safe_strerror(save_errno));
close (sock);
return -1;
}
@@ -226,8 +224,7 @@ netlink_socket (struct nlsock *nl, unsigned long groups, ns_id_t ns_id)
ret = getsockname (sock, (struct sockaddr *) &snl, (socklen_t *) &namelen);
if (ret < 0 || namelen != sizeof snl)
{
- zlog (NULL, LOG_ERR, "Can't get %s socket name: %s", nl->name,
- safe_strerror (errno));
+ zlog_err("Can't get %s socket name: %s", nl->name, safe_strerror(errno));
close (sock);
return -1;
}
@@ -244,7 +241,7 @@ netlink_information_fetch (struct sockaddr_nl *snl, struct nlmsghdr *h,
/* JF: Ignore messages that aren't from the kernel */
if ( snl->nl_pid != 0 )
{
- zlog ( NULL, LOG_ERR, "Ignoring message from pid %u", snl->nl_pid );
+ zlog_err("Ignoring message from pid %u", snl->nl_pid);
return 0;
}
@@ -493,8 +490,7 @@ netlink_parse_info (int (*filter) (struct sockaddr_nl *, struct nlmsghdr *,
continue;
if (errno == EWOULDBLOCK || errno == EAGAIN)
break;
- zlog (NULL, LOG_ERR, "%s recvmsg overrun: %s",
- nl->name, safe_strerror(errno));
+ zlog_err("%s recvmsg overrun: %s", nl->name, safe_strerror(errno));
/*
* In this case we are screwed.
* There is no good way to
@@ -506,14 +502,14 @@ netlink_parse_info (int (*filter) (struct sockaddr_nl *, struct nlmsghdr *,
if (status == 0)
{
- zlog (NULL, LOG_ERR, "%s EOF", nl->name);
+ zlog_err("%s EOF", nl->name);
return -1;
}
if (msg.msg_namelen != sizeof snl)
{
- zlog (NULL, LOG_ERR, "%s sender address length error: length %d",
- nl->name, msg.msg_namelen);
+ zlog_err("%s sender address length error: length %d", nl->name,
+ msg.msg_namelen);
return -1;
}
@@ -558,8 +554,7 @@ netlink_parse_info (int (*filter) (struct sockaddr_nl *, struct nlmsghdr *,
if (h->nlmsg_len < NLMSG_LENGTH (sizeof (struct nlmsgerr)))
{
- zlog (NULL, LOG_ERR, "%s error: message truncated",
- nl->name);
+ zlog_err("%s error: message truncated", nl->name);
return -1;
}
@@ -627,7 +622,7 @@ netlink_parse_info (int (*filter) (struct sockaddr_nl *, struct nlmsghdr *,
error = (*filter) (&snl, h, zns->ns_id, startup);
if (error < 0)
{
- zlog (NULL, LOG_ERR, "%s filter function error", nl->name);
+ zlog_err("%s filter function error", nl->name);
ret = error;
}
}
@@ -635,13 +630,12 @@ netlink_parse_info (int (*filter) (struct sockaddr_nl *, struct nlmsghdr *,
/* After error care. */
if (msg.msg_flags & MSG_TRUNC)
{
- zlog (NULL, LOG_ERR, "%s error: message truncated", nl->name);
+ zlog_err("%s error: message truncated", nl->name);
continue;
}
if (status)
{
- zlog (NULL, LOG_ERR, "%s error: data remnant size %d", nl->name,
- status);
+ zlog_err("%s error: data remnant size %d", nl->name, status);
return -1;
}
}
@@ -696,11 +690,11 @@ netlink_talk (int (*filter) (struct sockaddr_nl *, struct nlmsghdr *,
/* Send message to netlink interface. */
if (zserv_privs.change (ZPRIVS_RAISE))
- zlog (NULL, LOG_ERR, "Can't raise privileges");
+ zlog_err("Can't raise privileges");
status = sendmsg (nl->sock, &msg, 0);
save_errno = errno;
if (zserv_privs.change (ZPRIVS_LOWER))
- zlog (NULL, LOG_ERR, "Can't lower privileges");
+ zlog_err("Can't lower privileges");
if (IS_ZEBRA_DEBUG_KERNEL_MSGDUMP_SEND)
{
@@ -710,8 +704,7 @@ netlink_talk (int (*filter) (struct sockaddr_nl *, struct nlmsghdr *,
if (status < 0)
{
- zlog (NULL, LOG_ERR, "netlink_talk sendmsg() error: %s",
- safe_strerror (save_errno));
+ zlog_err("netlink_talk sendmsg() error: %s", safe_strerror(save_errno));
return -1;
}
@@ -740,7 +733,7 @@ netlink_request (int family, int type, struct nlsock *nl)
/* Check netlink socket. */
if (nl->sock < 0)
{
- zlog (NULL, LOG_ERR, "%s socket isn't active.", nl->name);
+ zlog_err("%s socket isn't active.", nl->name);
return -1;
}
@@ -760,7 +753,7 @@ netlink_request (int family, int type, struct nlsock *nl)
*/
if (zserv_privs.change (ZPRIVS_RAISE))
{
- zlog (NULL, LOG_ERR, "Can't raise privileges");
+ zlog_err("Can't raise privileges");
return -1;
}
@@ -769,12 +762,11 @@ netlink_request (int family, int type, struct nlsock *nl)
save_errno = errno;
if (zserv_privs.change (ZPRIVS_LOWER))
- zlog (NULL, LOG_ERR, "Can't lower privileges");
+ zlog_err("Can't lower privileges");
if (ret < 0)
{
- zlog (NULL, LOG_ERR, "%s sendto failed: %s", nl->name,
- safe_strerror (save_errno));
+ zlog_err("%s sendto failed: %s", nl->name, safe_strerror(save_errno));
return -1;
}
diff --git a/zebra/kernel_socket.c b/zebra/kernel_socket.c
index 20a0472195..1bb85a31ab 100644
--- a/zebra/kernel_socket.c
+++ b/zebra/kernel_socket.c
@@ -826,9 +826,8 @@ rtm_read_mesg (struct rt_msghdr *rtm,
/* rt_msghdr version check. */
if (rtm->rtm_version != RTM_VERSION)
- zlog (NULL, LOG_WARNING,
- "Routing message version different %d should be %d."
- "This may cause problem\n", rtm->rtm_version, RTM_VERSION);
+ zlog_warn("Routing message version different %d should be %d." "This may cause problem\n",
+ rtm->rtm_version, RTM_VERSION);
/* Be sure structure is cleared */
memset (dest, 0, sizeof (union sockunion));
@@ -852,7 +851,7 @@ rtm_read_mesg (struct rt_msghdr *rtm,
/* Assert read up to the end of pointer. */
if (pnt != end)
- zlog (NULL, LOG_WARNING, "rtm_read() doesn't read all socket data.");
+ zlog_warn("rtm_read() doesn't read all socket data.");
return rtm->rtm_flags;
}
diff --git a/zebra/main.c b/zebra/main.c
index b72ce84cd6..b70ce7eda7 100644
--- a/zebra/main.c
+++ b/zebra/main.c
@@ -35,7 +35,7 @@
#include "privs.h"
#include "sigevent.h"
#include "vrf.h"
-#include "sockopt.h"
+#include "libfrr.h"
#include "zebra/rib.h"
#include "zebra/zserv.h"
@@ -60,9 +60,6 @@ struct zebra_t zebrad =
/* process id. */
pid_t pid;
-/* VTY Socket prefix */
-char vty_sock_path[MAXPATHLEN] = ZEBRA_VTYSH_PATH;
-
/* Pacify zclient.o in libfrr, which expects this variable. */
struct thread_master *master;
@@ -81,30 +78,18 @@ u_int32_t nl_rcvbufsize = 4194304;
#endif /* HAVE_NETLINK */
/* Command line options. */
-#define OPTION_VTYSOCK 1000
struct option longopts[] =
{
{ "batch", no_argument, NULL, 'b'},
- { "daemon", no_argument, NULL, 'd'},
{ "allow_delete", no_argument, NULL, 'a'},
{ "keep_kernel", no_argument, NULL, 'k'},
{ "fpm_format", required_argument, NULL, 'F'},
- { "config_file", required_argument, NULL, 'f'},
- { "pid_file", required_argument, NULL, 'i'},
{ "socket", required_argument, NULL, 'z'},
- { "help", no_argument, NULL, 'h'},
- { "vty_addr", required_argument, NULL, 'A'},
- { "vty_port", required_argument, NULL, 'P'},
- { "vty_socket", required_argument, NULL, OPTION_VTYSOCK },
{ "ecmp", required_argument, NULL, 'e'},
{ "retain", no_argument, NULL, 'r'},
- { "dryrun", no_argument, NULL, 'C'},
#ifdef HAVE_NETLINK
{ "nl-bufsize", required_argument, NULL, 's'},
#endif /* HAVE_NETLINK */
- { "user", required_argument, NULL, 'u'},
- { "group", required_argument, NULL, 'g'},
- { "version", no_argument, NULL, 'v'},
{ 0 }
};
@@ -130,54 +115,8 @@ struct zebra_privs_t zserv_privs =
.cap_num_i = 0
};
-/* Default configuration file path. */
-char config_default[] = SYSCONFDIR DEFAULT_CONFIG_FILE;
-
-/* Process ID saved for use by init system */
-const char *pid_file = PATH_ZEBRA_PID;
-
unsigned int multipath_num = MULTIPATH_NUM;
-/* Help information display. */
-static void
-usage (char *progname, int status)
-{
- if (status != 0)
- fprintf (stderr, "Try `%s --help' for more information.\n", progname);
- else
- {
- printf ("Usage : %s [OPTION...]\n\n"\
- "Daemon which manages kernel routing table management and "\
- "redistribution between different routing protocols.\n\n"\
- "-b, --batch Runs in batch mode\n"\
- "-d, --daemon Runs in daemon mode\n"\
- "-a, --allow_delete Allow other processes to delete Quagga Routes\n" \
- "-f, --config_file Set configuration file name\n"\
- "-F, --fpm_format Set fpm format to 'netlink' or 'protobuf'\n"\
- "-i, --pid_file Set process identifier file name\n"\
- "-z, --socket Set path of zebra socket\n"\
- "-k, --keep_kernel Don't delete old routes which installed by "\
- "zebra.\n"\
- "-C, --dryrun Check configuration for validity and exit\n"\
- "-A, --vty_addr Set vty's bind address\n"\
- "-P, --vty_port Set vty's port number\n"\
- " --vty_socket Override vty socket path\n"\
- "-r, --retain When program terminates, retain added route "\
- "by zebra.\n"\
- "-u, --user User to run as\n"\
- "-g, --group Group to run as\n", progname);
-#ifdef HAVE_NETLINK
- printf ("-s, --nl-bufsize Set netlink receive buffer size\n");
-#endif /* HAVE_NETLINK */
- printf ("-v, --version Print program version\n"\
- "-h, --help Display this help and exit\n"\
- "\n"\
- "Report bugs to %s\n", FRR_BUG_ADDRESS);
- }
-
- exit (status);
-}
-
/* SIGHUP handler. */
static void
sighup (void)
@@ -229,8 +168,7 @@ sigint (void)
work_queue_free (zebrad.lsp_process_q);
meta_queue_free (zebrad.mq);
thread_master_free (zebrad.master);
- if (zlog_default)
- closezlog (zlog_default);
+ closezlog ();
exit (0);
}
@@ -239,7 +177,7 @@ sigint (void)
static void
sigusr1 (void)
{
- zlog_rotate (NULL);
+ zlog_rotate();
}
struct quagga_signal_t zebra_signals[] =
@@ -262,44 +200,49 @@ struct quagga_signal_t zebra_signals[] =
},
};
+FRR_DAEMON_INFO(zebra, ZEBRA,
+ .vty_port = ZEBRA_VTY_PORT,
+ .flags = FRR_NO_ZCLIENT,
+
+ .proghelp = "Daemon which manages kernel routing table management "
+ "and\nredistribution between different routing protocols.",
+
+ .signals = zebra_signals,
+ .n_signals = array_size(zebra_signals),
+
+ .privs = &zserv_privs,
+)
+
/* Main startup routine. */
int
main (int argc, char **argv)
{
- char *p;
- char *vty_addr = NULL;
- int vty_port = ZEBRA_VTY_PORT;
- int dryrun = 0;
- int batch_mode = 0;
- int daemon_mode = 0;
- char *config_file = NULL;
- char *progname;
- struct thread thread;
+ // int batch_mode = 0;
char *zserv_path = NULL;
char *fpm_format = NULL;
- /* Set umask before anything for security */
- umask (0027);
+ frr_preinit(&zebra_di, argc, argv);
- /* preserve my name */
- progname = ((p = strrchr (argv[0], '/')) ? ++p : argv[0]);
-
- zlog_default = openzlog (progname, ZLOG_ZEBRA, 0,
- LOG_CONS|LOG_NDELAY|LOG_PID, LOG_DAEMON);
- zprivs_init (&zserv_privs);
-#if defined(HAVE_CUMULUS)
- zlog_set_level (NULL, ZLOG_DEST_SYSLOG, zlog_default->default_lvl);
+ frr_opt_add("bakF:z:e:r"
+#ifdef HAVE_NETLINK
+ "s:"
#endif
+ , longopts,
+ " -b, --batch Runs in batch mode\n"
+ " -a, --allow_delete Allow other processes to delete Quagga Routes\n"
+ " -F, --fpm_format Set fpm format to 'netlink' or 'protobuf'\n"
+ " -z, --socket Set path of zebra socket\n"
+ " -e, --ecmp Specify ECMP to use.\n"
+ " -k, --keep_kernel Don't delete old routes which installed by zebra.\n"
+ " -r, --retain When program terminates, retain added route by zebra.\n"
+#ifdef HAVE_NETLINK
+ " -s, --nl-bufsize Set netlink receive buffer size\n"
+#endif /* HAVE_NETLINK */
+ );
- while (1)
+ while (1)
{
- int opt;
-
-#ifdef HAVE_NETLINK
- opt = getopt_long (argc, argv, "bdakf:F:i:z:hA:P:ru:g:vs:C", longopts, 0);
-#else
- opt = getopt_long (argc, argv, "bdakf:F:i:z:hA:P:ru:g:vC", longopts, 0);
-#endif /* HAVE_NETLINK */
+ int opt = frr_getopt(argc, argv, NULL);
if (opt == EOF)
break;
@@ -309,9 +252,7 @@ main (int argc, char **argv)
case 0:
break;
case 'b':
- batch_mode = 1;
- case 'd':
- daemon_mode = 1;
+ // batch_mode = 1;
break;
case 'a':
allow_delete = 1;
@@ -319,18 +260,9 @@ main (int argc, char **argv)
case 'k':
keep_kernel_mode = 1;
break;
- case 'C':
- dryrun = 1;
- break;
- case 'f':
- config_file = optarg;
- break;
case 'F':
fpm_format = optarg;
break;
- case 'A':
- vty_addr = optarg;
- break;
case 'e':
multipath_num = atoi (optarg);
if (multipath_num > MULTIPATH_NUM || multipath_num <= 0)
@@ -339,27 +271,9 @@ main (int argc, char **argv)
return 1;
}
break;
- case 'i':
- pid_file = optarg;
- break;
case 'z':
zserv_path = optarg;
break;
- case 'P':
- /* Deal with atoi() returning 0 on failure, and zebra not
- listening on zebra port... */
- if (strcmp(optarg, "0") == 0)
- {
- vty_port = 0;
- break;
- }
- vty_port = atoi (optarg);
- if (vty_port <= 0 || vty_port > 0xffff)
- vty_port = ZEBRA_VTY_PORT;
- break;
- case OPTION_VTYSOCK:
- set_socket_path(vty_sock_path, ZEBRA_VTYSH_PATH, optarg, sizeof (vty_sock_path));
- break;
case 'r':
retain_mode = 1;
break;
@@ -368,34 +282,14 @@ main (int argc, char **argv)
nl_rcvbufsize = atoi (optarg);
break;
#endif /* HAVE_NETLINK */
- case 'u':
- zserv_privs.user = optarg;
- break;
- case 'g':
- zserv_privs.group = optarg;
- break;
- case 'v':
- print_version (progname);
- exit (0);
- break;
- case 'h':
- usage (progname, 0);
- break;
default:
- usage (progname, 1);
+ frr_help_exit (1);
break;
}
}
- /* Make master thread emulator. */
- zebrad.master = thread_master_create ();
-
- /* Vty related initialize. */
- signal_init (zebrad.master, array_size(zebra_signals), zebra_signals);
- cmd_init (1);
vty_config_lockless ();
- vty_init (zebrad.master);
- memory_init ();
+ zebrad.master = frr_init();
/* Zebra related initialize. */
zebra_init ();
@@ -443,29 +337,11 @@ main (int argc, char **argv)
* The notifications from kernel will show originating PID equal
* to that after daemon() completes (if ever called).
*/
- vty_read_config (config_file, config_default);
+ frr_config_fork();
- /* Don't start execution if we are in dry-run mode */
- if (dryrun)
- return(0);
-
- /* Clean up rib. */
+ /* Clean up rib -- before fork (?) */
/* rib_weed_tables (); */
- /* Exit when zebra is working in batch mode. */
- if (batch_mode)
- exit (0);
-
- /* Daemonize. */
- if (daemon_mode && daemon (0, 0) < 0)
- {
- zlog_err("Zebra daemon failed: %s", strerror(errno));
- exit (1);
- }
-
- /* Output pid of zebra. */
- pid_output (pid_file);
-
/* After we have successfully acquired the pidfile, we can be sure
* about being the only copy of zebra process, which is submitting
* changes to the FIB.
@@ -483,14 +359,7 @@ main (int argc, char **argv)
/* This must be done only after locking pidfile (bug #403). */
zebra_zserv_socket_init (zserv_path);
- /* Make vty server socket. */
- vty_serv_sock (vty_addr, vty_port, vty_sock_path);
-
- /* Print banner. */
- zlog_notice ("Zebra %s starting: vty@%d", FRR_VERSION, vty_port);
-
- while (thread_fetch (zebrad.master, &thread))
- thread_call (&thread);
+ frr_run (zebrad.master);
/* Not reached... */
return 0;
diff --git a/zebra/rt_socket.c b/zebra/rt_socket.c
index a4cc4eed06..de8cc69a8e 100644
--- a/zebra/rt_socket.c
+++ b/zebra/rt_socket.c
@@ -398,12 +398,12 @@ kernel_route_rib (struct prefix *p, struct prefix *src_p,
if (src_p && src_p->prefixlen)
{
- zlog (NULL, LOG_ERR, "route add: IPv6 sourcedest routes unsupported!");
+ zlog_err ("route add: IPv6 sourcedest routes unsupported!");
return 1;
}
if (zserv_privs.change(ZPRIVS_RAISE))
- zlog (NULL, LOG_ERR, "Can't raise privileges");
+ zlog_err("Can't raise privileges");
if (old)
route |= kernel_rtm (RTM_DELETE, p, old);
@@ -412,7 +412,7 @@ kernel_route_rib (struct prefix *p, struct prefix *src_p,
route |= kernel_rtm (RTM_ADD, p, new);
if (zserv_privs.change(ZPRIVS_LOWER))
- zlog (NULL, LOG_ERR, "Can't lower privileges");
+ zlog_err("Can't lower privileges");
return route;
}
diff --git a/zebra/test_main.c b/zebra/test_main.c
index dea1df1697..a80b4c87c4 100644
--- a/zebra/test_main.c
+++ b/zebra/test_main.c
@@ -85,7 +85,7 @@ zebra_capabilities_t _caps_p [] =
char config_default[] = SYSCONFDIR DEFAULT_CONFIG_FILE;
/* Process ID saved for use by init system */
-const char *pid_file = PATH_ZEBRA_PID;
+const char *pid_file = "testzebra.pid";
/* Help information display. */
static void
@@ -181,7 +181,7 @@ sigint (void)
static void
sigusr1 (void)
{
- zlog_rotate (NULL);
+ zlog_rotate();
}
struct quagga_signal_t zebra_signals[] =
@@ -222,8 +222,7 @@ main (int argc, char **argv)
/* preserve my name */
progname = ((p = strrchr (argv[0], '/')) ? ++p : argv[0]);
- zlog_default = openzlog (progname, ZLOG_ZEBRA, 0,
- LOG_CONS|LOG_NDELAY|LOG_PID, LOG_DAEMON);
+ openzlog(progname, "ZEBRA", 0, LOG_CONS | LOG_NDELAY | LOG_PID, LOG_DAEMON);
while (1)
{
diff --git a/zebra/zebra_rib.c b/zebra/zebra_rib.c
index 08874f22fc..f9734fdf7d 100644
--- a/zebra/zebra_rib.c
+++ b/zebra/zebra_rib.c
@@ -28,6 +28,7 @@
#include "zebra_memory.h"
#include "command.h"
#include "log.h"
+#include "log_int.h"
#include "sockunion.h"
#include "linklist.h"
#include "thread.h"
@@ -78,6 +79,7 @@ static const struct
[ZEBRA_ROUTE_OSPF6] = {ZEBRA_ROUTE_OSPF6, 110},
[ZEBRA_ROUTE_ISIS] = {ZEBRA_ROUTE_ISIS, 115},
[ZEBRA_ROUTE_BGP] = {ZEBRA_ROUTE_BGP, 20 /* IBGP is 200. */},
+ [ZEBRA_ROUTE_NHRP] = {ZEBRA_ROUTE_NHRP, 10},
/* no entry/default: 150 */
};
@@ -110,7 +112,7 @@ _rnode_zlog(const char *_func, vrf_id_t vrf_id, struct route_node *rn, int prior
snprintf(buf, sizeof(buf), "{(route_node *) NULL}");
}
- zlog (NULL, priority, "%s: %d:%s: %s", _func, vrf_id, buf, msgbuf);
+ zlog (priority, "%s: %d:%s: %s", _func, vrf_id, buf, msgbuf);
}
#define rnode_debug(node, vrf_id, ...) \
@@ -1822,6 +1824,7 @@ static const u_char meta_queue_map[ZEBRA_ROUTE_MAX] = {
[ZEBRA_ROUTE_OSPF] = 2,
[ZEBRA_ROUTE_OSPF6] = 2,
[ZEBRA_ROUTE_ISIS] = 2,
+ [ZEBRA_ROUTE_NHRP] = 2,
[ZEBRA_ROUTE_BGP] = 3,
[ZEBRA_ROUTE_HSLS] = 4,
[ZEBRA_ROUTE_TABLE] = 1,
diff --git a/zebra/zebra_rnh.c b/zebra/zebra_rnh.c
index f42354b6e6..71ee8975b5 100644
--- a/zebra/zebra_rnh.c
+++ b/zebra/zebra_rnh.c
@@ -376,19 +376,28 @@ zebra_rnh_resolve_entry (vrf_id_t vrfid, int family, rnh_type_t type,
{
if (CHECK_FLAG (rib->status, RIB_ENTRY_REMOVED))
continue;
- if (CHECK_FLAG (rib->flags, ZEBRA_FLAG_SELECTED))
+ if (! CHECK_FLAG (rib->status, RIB_ENTRY_SELECTED_FIB))
+ continue;
+
+ if (CHECK_FLAG(rnh->flags, ZEBRA_NHT_CONNECTED))
{
- if (CHECK_FLAG(rnh->flags, ZEBRA_NHT_CONNECTED))
+ if (rib->type == ZEBRA_ROUTE_CONNECT)
+ break;
+ if (rib->type == ZEBRA_ROUTE_NHRP)
{
- if (rib->type == ZEBRA_ROUTE_CONNECT)
+ struct nexthop *nexthop;
+ for (nexthop = rib->nexthop; nexthop; nexthop = nexthop->next)
+ if (nexthop->type == NEXTHOP_TYPE_IFINDEX)
+ break;
+ if (nexthop)
break;
}
- else if ((type == RNH_IMPORT_CHECK_TYPE) &&
- (rib->type == ZEBRA_ROUTE_BGP))
- continue;
- else
- break;
}
+ else if ((type == RNH_IMPORT_CHECK_TYPE) &&
+ (rib->type == ZEBRA_ROUTE_BGP))
+ continue;
+ else
+ break;
}
}
diff --git a/zebra/zebra_vty.c b/zebra/zebra_vty.c
index 17d8249906..ddb037335c 100644
--- a/zebra/zebra_vty.c
+++ b/zebra/zebra_vty.c
@@ -684,6 +684,7 @@ vty_show_ip_route_detail (struct vty *vty, struct route_node *rn, int mcast)
if (rib->type == ZEBRA_ROUTE_RIP
|| rib->type == ZEBRA_ROUTE_OSPF
|| rib->type == ZEBRA_ROUTE_ISIS
+ || rib->type == ZEBRA_ROUTE_NHRP
|| rib->type == ZEBRA_ROUTE_TABLE
|| rib->type == ZEBRA_ROUTE_BGP)
{
@@ -835,6 +836,7 @@ vty_show_ip_route (struct vty *vty, struct route_node *rn, struct rib *rib,
if (rib->type == ZEBRA_ROUTE_RIP
|| rib->type == ZEBRA_ROUTE_OSPF
|| rib->type == ZEBRA_ROUTE_ISIS
+ || rib->type == ZEBRA_ROUTE_NHRP
|| rib->type == ZEBRA_ROUTE_TABLE
|| rib->type == ZEBRA_ROUTE_BGP)
{
@@ -1041,6 +1043,7 @@ vty_show_ip_route (struct vty *vty, struct route_node *rn, struct rib *rib,
if (rib->type == ZEBRA_ROUTE_RIP
|| rib->type == ZEBRA_ROUTE_OSPF
|| rib->type == ZEBRA_ROUTE_ISIS
+ || rib->type == ZEBRA_ROUTE_NHRP
|| rib->type == ZEBRA_ROUTE_TABLE
|| rib->type == ZEBRA_ROUTE_BGP)
{
diff --git a/zebra/zserv.c b/zebra/zserv.c
index 60364410a8..064489acd9 100644
--- a/zebra/zserv.c
+++ b/zebra/zserv.c
@@ -2169,7 +2169,7 @@ zebra_serv ()
sockopt_reuseport (accept_sock);
if ( zserv_privs.change(ZPRIVS_RAISE) )
- zlog (NULL, LOG_ERR, "Can't raise privileges");
+ zlog_err("Can't raise privileges");
ret = bind (accept_sock, (struct sockaddr *)&addr,
sizeof (struct sockaddr_in));
@@ -2183,7 +2183,7 @@ zebra_serv ()
}
if ( zserv_privs.change(ZPRIVS_LOWER) )
- zlog (NULL, LOG_ERR, "Can't lower privileges");
+ zlog_err("Can't lower privileges");
ret = listen (accept_sock, 1);
if (ret < 0)