diff options
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 \ @@ -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); @@ -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 */ @@ -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; } @@ -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; @@ -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. */ @@ -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(®->reglist_entry); + list_add_tail(®->reglist_entry, &nhs->reglist_head); + nhrp_peer_notify_add(reg->peer, ®->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); @@ -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) |
