This is useful in other places too, e.g. for BMP outbound connections.
Signed-off-by: David Lamparter <equinox@opensourcerouting.org>
AC_MSG_ERROR([trying to build nhrpd, but libcares not found. install c-ares and its -dev headers.])
])
fi
-
+AM_CONDITIONAL([CARES], [test "${NHRPD}" != ""])
dnl ------------------
dnl check Net-SNMP library
usr/bin/vtysh
usr/bin/mtracebis
usr/lib/*/frr/libfrr.*
+usr/lib/*/frr/libfrrcares.*
usr/lib/*/frr/libfrrospfapiclient.*
usr/lib/frr/*.sh
usr/lib/frr/*d
"northbound debug", // NORTHBOUND_DEBUG_NODE,
"vnc debug", // DEBUG_VNC_NODE,
"route-map debug", /* RMAP_DEBUG_NODE */
+ "resolver debug", /* RESOLVER_DEBUG_NODE */
"aaa", // AAA_NODE,
"keychain", // KEYCHAIN_NODE,
"keychain key", // KEYCHAIN_KEY_NODE,
NORTHBOUND_DEBUG_NODE, /* Northbound Debug node. */
DEBUG_VNC_NODE, /* Debug VNC node. */
RMAP_DEBUG_NODE, /* Route-map debug node */
+ RESOLVER_DEBUG_NODE, /* Resolver debug node */
AAA_NODE, /* AAA node. */
KEYCHAIN_NODE, /* Key-chain node. */
KEYCHAIN_KEY_NODE, /* Key-chain key node. */
.description = "A callback used to process a configuration change has returned an error while applying the changes",
.suggestion = "Gather log data and open an Issue.",
},
+ {
+ .code = EC_LIB_RESOLVER,
+ .title = "DNS Resolution",
+ .description = "An error was detected while attempting to resolve a hostname",
+ .suggestion = "Ensure that DNS is working properly and the hostname is configured in dns. If you are still seeing this error, open an issue"
+ },
{
.code = END_FERR,
}
EC_LIB_GRPC_INIT,
EC_LIB_ID_CONSISTENCY,
EC_LIB_ID_EXHAUST,
+ EC_LIB_RESOLVER,
};
extern void lib_error_init(void);
--- /dev/null
+/* 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.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <ares.h>
+#include <ares_version.h>
+
+#include "vector.h"
+#include "thread.h"
+#include "lib_errors.h"
+#include "resolver.h"
+#include "command.h"
+
+struct resolver_state {
+ ares_channel channel;
+ struct thread_master *master;
+ struct thread *timeout;
+ vector read_threads, write_threads;
+};
+
+static struct resolver_state state;
+static bool resolver_debug;
+
+#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_add_read(r->master, resolver_cb_socket_readable, r, fd,
+ &t);
+ 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_add_write(r->master, resolver_cb_socket_writable, r, fd,
+ &t);
+ 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_add_timer_msec(r->master, resolver_cb_timeout, r,
+ timeoutms, &r->timeout);
+ }
+}
+
+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_add_read(r->master, resolver_cb_socket_readable,
+ r, fd, &t);
+ 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_add_read(r->master, resolver_cb_socket_writable,
+ r, fd, &t);
+ 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);
+ }
+ }
+}
+
+
+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) {
+ if (resolver_debug)
+ zlog_debug("[%p] Resolving failed", query);
+
+ query->callback(query, -1, NULL);
+ query->callback = NULL;
+ return;
+ }
+
+ for (i = 0; i < array_size(addr) && he->h_addr_list[i] != NULL; 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;
+ }
+ }
+
+ if (resolver_debug)
+ zlog_debug("[%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) {
+ flog_err(
+ EC_LIB_RESOLVER,
+ "Trying to resolve '%s', but previous query was not finished yet",
+ hostname);
+ return;
+ }
+
+ if (resolver_debug)
+ zlog_debug("[%p] Resolving '%s'", query, hostname);
+
+ query->callback = callback;
+ ares_gethostbyname(state.channel, hostname, af, ares_address_cb, query);
+ resolver_update_timeouts(&state);
+}
+
+DEFUN(debug_resolver,
+ debug_resolver_cmd,
+ "[no] debug resolver",
+ NO_STR
+ DEBUG_STR
+ "Debug DNS resolver actions\n")
+{
+ resolver_debug = (argc == 2);
+ return CMD_SUCCESS;
+}
+
+static struct cmd_node resolver_debug_node = {RESOLVER_DEBUG_NODE, "", 1};
+
+static int resolver_config_write_debug(struct vty *vty)
+{
+ if (resolver_debug)
+ vty_out(vty, "debug resolver\n");
+ return 1;
+}
+
+
+void resolver_init(struct thread_master *tm)
+{
+ struct ares_options ares_opts;
+
+ state.master = tm;
+ 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);
+
+ install_node(&resolver_debug_node, resolver_config_write_debug);
+ install_element(CONFIG_NODE, &debug_resolver_cmd);
+ install_element(ENABLE_NODE, &debug_resolver_cmd);
+}
--- /dev/null
+/* 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.
+ */
+
+#ifndef _FRR_RESOLVER_H
+#define _FRR_RESOLVER_H
+
+#include "thread.h"
+#include "sockunion.h"
+
+struct resolver_query {
+ void (*callback)(struct resolver_query *, int n, union sockunion *);
+};
+
+void resolver_init(struct thread_master *tm);
+void resolver_resolve(struct resolver_query *query, int af,
+ const char *hostname, void (*cb)(struct resolver_query *,
+ int, union sockunion *));
+
+#endif /* _FRR_RESOLVER_H */
lib/snmp.c \
# end
+#
+# c-ares support
+#
+if CARES
+lib_LTLIBRARIES += lib/libfrrcares.la
+pkginclude_HEADERS += lib/resolver.h
+endif
+
+lib_libfrrcares_la_CFLAGS = $(WERROR) $(CARES_CFLAGS)
+lib_libfrrcares_la_LDFLAGS = -version-info 0:0:0
+lib_libfrrcares_la_LIBADD = $(CARES_LIBS)
+lib_libfrrcares_la_SOURCES = \
+ lib/resolver.c \
+ #end
+
#
# ZeroMQ support
#
.description = "NHRP has detected a error with the Strongswan code",
.suggestion = "Ensure that StrongSwan is configured correctly. Restart StrongSwan and FRR"
},
- {
- .code = EC_NHRP_RESOLVER,
- .title = "NHRP DNS Resolution",
- .description = "NHRP has detected an error in an attempt to resolve a hostname",
- .suggestion = "Ensure that DNS is working properly and the hostname is configured in dns. If you are still seeing this error, open an issue"
- },
{
.code = END_FERR,
}
enum nhrp_log_refs {
EC_NHRP_SWAN = NHRP_FERR_START,
- EC_NHRP_RESOLVER,
};
extern void nhrp_error_init(void);
nhrp_error_init();
vrf_init(NULL, NULL, NULL, NULL, NULL);
nhrp_interface_init();
- resolver_init();
+ resolver_init(master);
/* Run with elevated capabilities, as for all netlink activity
* we need privileges anyway. */
#include "zclient.h"
#include "debug.h"
#include "memory.h"
+#include "resolver.h"
DECLARE_MGROUP(NHRPD)
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);
+++ /dev/null
-/* 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.
- */
-
-#ifdef HAVE_CONFIG_H
-#include "config.h"
-#endif
-
-#include <ares.h>
-#include <ares_version.h>
-
-#include "vector.h"
-#include "thread.h"
-#include "lib_errors.h"
-
-#include "nhrpd.h"
-#include "nhrp_errors.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_add_read(master, resolver_cb_socket_readable, r, fd, &t);
- 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_add_write(master, resolver_cb_socket_writable, r, fd,
- &t);
- 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_add_timer_msec(master, resolver_cb_timeout, r, timeoutms,
- &r->timeout);
- }
-}
-
-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_add_read(master, resolver_cb_socket_readable, r,
- fd, &t);
- 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_add_read(master, resolver_cb_socket_writable, r,
- fd, &t);
- 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; i < array_size(addr) && he->h_addr_list[i] != NULL; 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) {
- flog_err(
- EC_NHRP_RESOLVER,
- "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);
-}
man8 += $(MANBUILD)/nhrpd.8
endif
-nhrpd_nhrpd_LDADD = lib/libfrr.la $(LIBCAP) $(CARES_LIBS)
-nhrpd_nhrpd_CFLAGS = $(AM_CFLAGS) $(CARES_CFLAGS)
+nhrpd_nhrpd_LDADD = lib/libfrr.la lib/libfrrcares.la $(LIBCAP)
nhrpd_nhrpd_SOURCES = \
nhrpd/linux.c \
nhrpd/netlink_arp.c \
nhrpd/nhrp_vc.c \
nhrpd/nhrp_vty.c \
nhrpd/reqid.c \
- nhrpd/resolver.c \
nhrpd/vici.c \
nhrpd/zbuf.c \
nhrpd/znl.c \
strlen("debug route-map"))
== 0)
config = config_get(RMAP_DEBUG_NODE, line);
+ else if (strncmp(line, "debug resolver",
+ strlen("debug resolver")) == 0)
+ config = config_get(RESOLVER_DEBUG_NODE, line);
else if (strncmp(line, "debug", strlen("debug")) == 0)
config = config_get(DEBUG_NODE, line);
else if (strncmp(line, "password", strlen("password")) == 0
|| (I) == PREFIX_IPV6_NODE || (I) == FORWARDING_NODE \
|| (I) == DEBUG_NODE || (I) == AAA_NODE || (I) == VRF_DEBUG_NODE \
|| (I) == NORTHBOUND_DEBUG_NODE || (I) == RMAP_DEBUG_NODE \
- || (I) == MPLS_NODE)
+ || (I) == RESOLVER_DEBUG_NODE || (I) == MPLS_NODE)
/* Display configuration to file pointer. */
void vtysh_config_dump(void)