From 8d70939786c1717c02f1e6302b44c01e9fce3d85 Mon Sep 17 00:00:00 2001 From: Henning Rogge Date: Mon, 1 Oct 2018 06:23:54 +0200 Subject: [PATCH 01/63] Add route_modifier plugin to olsrd2 --- apps/olsrd2/CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/olsrd2/CMakeLists.txt b/apps/olsrd2/CMakeLists.txt index 483a1d0d..4405c83b 100644 --- a/apps/olsrd2/CMakeLists.txt +++ b/apps/olsrd2/CMakeLists.txt @@ -62,6 +62,7 @@ IF (NOT OONF_STATIC_PLUGINS) http mpr remotecontrol + route_modifier ) ENDIF (NOT OONF_STATIC_PLUGINS) From a1a4c7e4b1ae001b352ff6a8f887d9c47613cf98 Mon Sep 17 00:00:00 2001 From: Martin Chauchet Date: Wed, 10 Oct 2018 10:03:55 +0200 Subject: [PATCH 02/63] Fixed scaling factor in methods: _initialize_time_values(): 3 -> 1000 _initialize_memory_values(): 0 -> 1 _initialize_timer_values(): 0 -> 1 _initialize_socket_values(): 0 -> 1 _initialize_logging_values(): 0 -> 1 --- src/generic/systeminfo/systeminfo.c | 30 ++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/src/generic/systeminfo/systeminfo.c b/src/generic/systeminfo/systeminfo.c index e8076c88..a342e3bb 100644 --- a/src/generic/systeminfo/systeminfo.c +++ b/src/generic/systeminfo/systeminfo.c @@ -73,7 +73,7 @@ static enum oonf_telnet_result _cb_systeminfo_help(struct oonf_telnet_data *con) static void _initialize_time_values(struct oonf_viewer_template *template); static void _initialize_version_values(struct oonf_viewer_template *template); -static void _initialize_memory_values(struct oonf_viewer_template *template, struct oonf_class *c); +static void _initialize_memory_values(struct oonf_viewer_template *template, struct oonf_class *cl); static void _initialize_timer_values(struct oonf_viewer_template *template, struct oonf_timer_class *tc); static void _initialize_socket_values(struct oonf_viewer_template *template, struct oonf_socket_entry *sock); static void _initialize_logging_values(struct oonf_viewer_template *template, enum oonf_log_source source); @@ -437,7 +437,7 @@ _cb_systeminfo_help(struct oonf_telnet_data *con) { static void _initialize_time_values(struct oonf_viewer_template *template) { oonf_log_get_walltime(&_value_system_time); - isonumber_from_u64(&_value_internal_time, oonf_clock_getNow(), "", 3, template->create_raw); + isonumber_from_u64(&_value_internal_time, oonf_clock_getNow(), "", 1000, template->create_raw); } /** @@ -456,10 +456,10 @@ static void _initialize_memory_values(struct oonf_viewer_template *template, struct oonf_class *cl) { strscpy(_value_stat_name, cl->name, sizeof(_value_stat_name)); - isonumber_from_u64(&_value_memory_usage, oonf_class_get_usage(cl), "", 0, template->create_raw); - isonumber_from_u64(&_value_memory_freelist, oonf_class_get_free(cl), "", 0, template->create_raw); - isonumber_from_u64(&_value_memory_alloc, oonf_class_get_allocations(cl), "", 0, template->create_raw); - isonumber_from_u64(&_value_memory_recycled, oonf_class_get_recycled(cl), "", 0, template->create_raw); + isonumber_from_u64(&_value_memory_usage, oonf_class_get_usage(cl), "", 1, template->create_raw); + isonumber_from_u64(&_value_memory_freelist, oonf_class_get_free(cl), "", 1, template->create_raw); + isonumber_from_u64(&_value_memory_alloc, oonf_class_get_allocations(cl), "", 1, template->create_raw); + isonumber_from_u64(&_value_memory_recycled, oonf_class_get_recycled(cl), "", 1, template->create_raw); } /** @@ -469,22 +469,22 @@ static void _initialize_timer_values(struct oonf_viewer_template *template, struct oonf_timer_class *tc) { strscpy(_value_stat_name, tc->name, sizeof(_value_stat_name)); - isonumber_from_u64(&_value_timer_usage, oonf_timer_get_usage(tc), "", 0, template->create_raw); - isonumber_from_u64(&_value_timer_change, oonf_timer_get_changes(tc), "", 0, template->create_raw); - isonumber_from_u64(&_value_timer_fire, oonf_timer_get_fired(tc), "", 0, template->create_raw); - isonumber_from_u64(&_value_timer_long, oonf_timer_get_long(tc), "", 0, template->create_raw); + isonumber_from_u64(&_value_timer_usage, oonf_timer_get_usage(tc), "", 1, template->create_raw); + isonumber_from_u64(&_value_timer_change, oonf_timer_get_changes(tc), "", 1, template->create_raw); + isonumber_from_u64(&_value_timer_fire, oonf_timer_get_fired(tc), "", 1, template->create_raw); + isonumber_from_u64(&_value_timer_long, oonf_timer_get_long(tc), "", 1, template->create_raw); } /** - * Initialize the value buffers for a timer class + * Initialize the value buffers for a socket class */ static void _initialize_socket_values(struct oonf_viewer_template *template, struct oonf_socket_entry *sock) { strscpy(_value_stat_name, sock->name, sizeof(_value_stat_name)); - isonumber_from_u64(&_value_socket_recv, oonf_socket_get_recv(sock), "", 0, template->create_raw); - isonumber_from_u64(&_value_socket_send, oonf_socket_get_send(sock), "", 0, template->create_raw); - isonumber_from_u64(&_value_socket_long, oonf_socket_get_long(sock), "", 0, template->create_raw); + isonumber_from_u64(&_value_socket_recv, oonf_socket_get_recv(sock), "", 1, template->create_raw); + isonumber_from_u64(&_value_socket_send, oonf_socket_get_send(sock), "", 1, template->create_raw); + isonumber_from_u64(&_value_socket_long, oonf_socket_get_long(sock), "", 1, template->create_raw); } /** @@ -495,7 +495,7 @@ _initialize_socket_values(struct oonf_viewer_template *template, struct oonf_soc static void _initialize_logging_values(struct oonf_viewer_template *template, enum oonf_log_source source) { strscpy(_value_log_source, LOG_SOURCE_NAMES[source], sizeof(_value_log_source)); - isonumber_from_u64(&_value_log_warnings, oonf_log_get_warning_count(source), "", 0, template->create_raw); + isonumber_from_u64(&_value_log_warnings, oonf_log_get_warning_count(source), "", 1, template->create_raw); } /** From a661b232ac9b38cbf7184c3b34cd3501f3326323 Mon Sep 17 00:00:00 2001 From: Henning Rogge Date: Wed, 7 Nov 2018 06:24:08 +0100 Subject: [PATCH 03/63] Fix definition of ethernet multicast groups --- apps/dlep-radio/CMakeLists.txt | 2 ++ include/oonf/libcommon/netaddr.h | 4 ++-- src/libcommon/netaddr.c | 4 ++-- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/apps/dlep-radio/CMakeLists.txt b/apps/dlep-radio/CMakeLists.txt index 529fa63f..4e887c21 100644 --- a/apps/dlep-radio/CMakeLists.txt +++ b/apps/dlep-radio/CMakeLists.txt @@ -34,6 +34,7 @@ IF (NOT OONF_STATIC_PLUGINS) socket stream_socket telnet + http timer viewer os_clock @@ -44,6 +45,7 @@ IF (NOT OONF_STATIC_PLUGINS) cfg_compact layer2info systeminfo + remotecontrol layer2_config layer2_import dlep_radio diff --git a/include/oonf/libcommon/netaddr.h b/include/oonf/libcommon/netaddr.h index 805ae47a..53eb81e8 100644 --- a/include/oonf/libcommon/netaddr.h +++ b/include/oonf/libcommon/netaddr.h @@ -172,8 +172,8 @@ EXPORT extern const struct netaddr NETADDR_IPV6_IPV4COMPATIBLE; EXPORT extern const struct netaddr NETADDR_IPV6_IPV4MAPPED; EXPORT extern const struct netaddr NETADDR_IPV6_LOOPBACK; EXPORT extern const struct netaddr NETADDR_MAC48_BROADCAST; -EXPORT const struct netaddr NETADDR_MAC48_IPV4_MULTICAST; -EXPORT const struct netaddr NETADDR_MAC48_IPV6_MULTICAST; +EXPORT extern const struct netaddr NETADDR_MAC48_IPV4_MULTICAST; +EXPORT extern const struct netaddr NETADDR_MAC48_IPV6_MULTICAST; EXPORT extern const union netaddr_socket NETADDR_SOCKET_IPV4_ANY; EXPORT extern const union netaddr_socket NETADDR_SOCKET_IPV6_ANY; diff --git a/src/libcommon/netaddr.c b/src/libcommon/netaddr.c index 47fc936a..077f387a 100644 --- a/src/libcommon/netaddr.c +++ b/src/libcommon/netaddr.c @@ -115,10 +115,10 @@ const struct netaddr NETADDR_MAC48_BROADCAST = { { 0xff, 0xff, 0xff, 0xff, 0xff, AF_MAC48, 48 }; /*! Ethernet multicast prefix for IPv4 multicast */ -const struct netaddr NETADDR_MAC48_IPV4_MULTICAST = { { 0x01, 0x00, 0x5e, 0x00, 0x00, 0x00 }, AF_MAC48, 25 }; +const struct netaddr NETADDR_MAC48_IPV4_MULTICAST = { { 0x01, 0x00, 0x5e, 0x00, 0x00, 0x00, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, AF_MAC48, 25 }; /*! Ethernet multicast prefix for IPv4 multicast */ -const struct netaddr NETADDR_MAC48_IPV6_MULTICAST = { { 0x33, 0x33, 0x00, 0x00, 0x00, 0x00 }, AF_MAC48, 16 }; +const struct netaddr NETADDR_MAC48_IPV6_MULTICAST = { { 0x33, 0x33, 0x00, 0x00, 0x00, 0x00, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, AF_MAC48, 16 }; /*! socket for binding to any IPv4 address */ const union netaddr_socket NETADDR_SOCKET_IPV4_ANY = { .v4 = { From 37e029c918328b6f1dfd12e331c6dd1596c3e300 Mon Sep 17 00:00:00 2001 From: Henning Rogge Date: Wed, 7 Nov 2018 09:54:46 +0100 Subject: [PATCH 04/63] copy packet socket interface name for os_interface listener --- src/base/oonf_packet_socket.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/base/oonf_packet_socket.c b/src/base/oonf_packet_socket.c index 32c59741..3b05b198 100644 --- a/src/base/oonf_packet_socket.c +++ b/src/base/oonf_packet_socket.c @@ -312,6 +312,7 @@ oonf_packet_apply_managed(struct oonf_packet_managed *managed, const struct oonf os_interface_remove(&managed->_if_listener); /* create new interface listener */ + managed->_if_listener.name = managed->_managed_config.interface; managed->_if_listener.mesh = managed->_managed_config.mesh; os_interface_add(&managed->_if_listener); } From b40c949b111952e741b6d4305b47c1ab4ba939c1 Mon Sep 17 00:00:00 2001 From: Henning Rogge Date: Fri, 9 Nov 2018 14:08:35 +0100 Subject: [PATCH 05/63] copy acl configuration from managed stream socket into all sub-sockets --- src/base/oonf_stream_socket.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/base/oonf_stream_socket.c b/src/base/oonf_stream_socket.c index 716c1f25..ca08fe4e 100644 --- a/src/base/oonf_stream_socket.c +++ b/src/base/oonf_stream_socket.c @@ -344,6 +344,9 @@ oonf_stream_add_managed(struct oonf_stream_managed *managed) { if (managed->config.session_timeout == 0) { managed->config.session_timeout = 120000; } + if (managed->config.acl == NULL) { + managed->config.acl = &managed->_managed_config.acl; + } managed->_if_listener.if_changed = _cb_interface_listener; managed->_if_listener.name = managed->_managed_config.interface; From a7904f2ee6365745060fc0e57812a5a1aac9ea8a Mon Sep 17 00:00:00 2001 From: Henning Rogge Date: Wed, 16 Jan 2019 09:08:16 +0100 Subject: [PATCH 06/63] First code revision that does not freeze the netlink sockets in automatic test setup --- include/oonf/base/oonf_class.h | 2 +- include/oonf/base/oonf_duplicate_set.h | 2 +- include/oonf/base/oonf_http.h | 2 +- include/oonf/base/oonf_layer2.h | 2 +- include/oonf/base/oonf_packet_socket.h | 2 +- include/oonf/base/oonf_rfc5444.h | 2 +- include/oonf/base/oonf_socket.h | 37 +- include/oonf/base/oonf_socket_data.h | 87 +++ include/oonf/base/oonf_stream_socket.h | 2 +- include/oonf/base/os_clock.h | 12 +- include/oonf/base/os_fd.h | 25 +- include/oonf/base/os_fd_data.h | 60 ++ include/oonf/base/os_interface.h | 24 +- include/oonf/base/os_linux/os_clock_linux.h | 1 + .../oonf/base/os_linux/os_clock_linux_data.h | 50 ++ include/oonf/base/os_linux/os_fd_linux.h | 36 +- include/oonf/base/os_linux/os_fd_linux_data.h | 84 +++ .../oonf/base/os_linux/os_interface_linux.h | 9 +- ...x_internal.h => os_interface_linux_data.h} | 26 +- include/oonf/base/os_linux/os_routing_linux.h | 23 +- .../base/os_linux/os_routing_linux_data.h | 78 +++ include/oonf/base/os_linux/os_system_linux.h | 108 +-- .../oonf/base/os_linux/os_system_linux_data.h | 175 +++++ include/oonf/base/os_linux/os_tunnel_linux.h | 3 +- .../oonf/base/os_linux/os_tunnel_linux_data.h | 52 ++ include/oonf/base/os_linux/os_vif_linux.h | 8 +- .../oonf/base/os_linux/os_vif_linux_data.h | 57 ++ include/oonf/base/os_routing.h | 32 +- include/oonf/base/os_system.h | 16 +- include/oonf/base/os_tunnel.h | 13 +- include/oonf/base/os_vif.h | 11 +- .../nl80211_listener/nl80211_get_interface.h | 2 +- .../nl80211_listener/nl80211_get_mpp.h | 2 +- .../nl80211_get_station_dump.h | 2 +- .../nl80211_listener/nl80211_get_survey.h | 2 +- .../nl80211_listener/nl80211_get_wiphy.h | 2 +- .../os_routing_generic_rt_to_string.c | 1 + src/base/os_linux/os_interface_linux.c | 351 +++------- src/base/os_linux/os_routing_linux.c | 239 ++++--- src/base/os_linux/os_system_linux.c | 656 ++++++++++-------- .../nl80211_listener/nl80211_get_interface.c | 5 +- .../nl80211_listener/nl80211_get_mpp.c | 6 +- .../nl80211_get_station_dump.c | 6 +- .../nl80211_listener/nl80211_get_survey.c | 6 +- .../nl80211_listener/nl80211_get_wiphy.c | 8 +- .../nl80211_listener/nl80211_listener.c | 62 +- 46 files changed, 1391 insertions(+), 1000 deletions(-) create mode 100644 include/oonf/base/oonf_socket_data.h create mode 100644 include/oonf/base/os_fd_data.h create mode 100644 include/oonf/base/os_linux/os_clock_linux_data.h create mode 100644 include/oonf/base/os_linux/os_fd_linux_data.h rename include/oonf/base/os_linux/{os_interface_linux_internal.h => os_interface_linux_data.h} (84%) create mode 100644 include/oonf/base/os_linux/os_routing_linux_data.h create mode 100644 include/oonf/base/os_linux/os_system_linux_data.h create mode 100644 include/oonf/base/os_linux/os_tunnel_linux_data.h create mode 100644 include/oonf/base/os_linux/os_vif_linux_data.h diff --git a/include/oonf/base/oonf_class.h b/include/oonf/base/oonf_class.h index 87eec731..37379112 100644 --- a/include/oonf/base/oonf_class.h +++ b/include/oonf/base/oonf_class.h @@ -46,8 +46,8 @@ #ifndef _OONF_CLASS_H #define _OONF_CLASS_H -#include #include +#include #include /*! subsystem identifier */ diff --git a/include/oonf/base/oonf_duplicate_set.h b/include/oonf/base/oonf_duplicate_set.h index 86c53475..06f08d87 100644 --- a/include/oonf/base/oonf_duplicate_set.h +++ b/include/oonf/base/oonf_duplicate_set.h @@ -46,8 +46,8 @@ #ifndef OONF_DUPLICATE_SET_H_ #define OONF_DUPLICATE_SET_H_ -#include #include +#include #include #include diff --git a/include/oonf/base/oonf_http.h b/include/oonf/base/oonf_http.h index d9efa659..3db523f3 100644 --- a/include/oonf/base/oonf_http.h +++ b/include/oonf/base/oonf_http.h @@ -46,8 +46,8 @@ #ifndef OONF_HTTP_H_ #define OONF_HTTP_H_ -#include #include +#include #include #include #include diff --git a/include/oonf/base/oonf_layer2.h b/include/oonf/base/oonf_layer2.h index 4dd967a1..a5a6a9ef 100644 --- a/include/oonf/base/oonf_layer2.h +++ b/include/oonf/base/oonf_layer2.h @@ -46,8 +46,8 @@ #ifndef OONF_LAYER2_H_ #define OONF_LAYER2_H_ -#include #include +#include #include #include diff --git a/include/oonf/base/oonf_packet_socket.h b/include/oonf/base/oonf_packet_socket.h index ef509d67..10286c4a 100644 --- a/include/oonf/base/oonf_packet_socket.h +++ b/include/oonf/base/oonf_packet_socket.h @@ -46,8 +46,8 @@ #ifndef OONF_PACKET_SOCKET_H_ #define OONF_PACKET_SOCKET_H_ -#include #include +#include #include #include #include diff --git a/include/oonf/base/oonf_rfc5444.h b/include/oonf/base/oonf_rfc5444.h index b9c773f1..ae1d6429 100644 --- a/include/oonf/base/oonf_rfc5444.h +++ b/include/oonf/base/oonf_rfc5444.h @@ -46,8 +46,8 @@ #ifndef OONF_RFC5444_H_ #define OONF_RFC5444_H_ -#include #include +#include #include #include #include diff --git a/include/oonf/base/oonf_socket.h b/include/oonf/base/oonf_socket.h index 8218b08c..eaafede1 100644 --- a/include/oonf/base/oonf_socket.h +++ b/include/oonf/base/oonf_socket.h @@ -46,47 +46,18 @@ #ifndef OONF_SOCKET_H_ #define OONF_SOCKET_H_ -#include +struct oonf_socket_entry; + #include +#include #include #include #include +#include /*! subsystem identifier */ #define OONF_SOCKET_SUBSYSTEM "socket" -/** - * registered socket handler - */ -struct oonf_socket_entry { - /*! name of socket handler */ - const char *name; - - /*! file descriptor of the socket */ - struct os_fd fd; - - /** - * Callback when read or write event happens to socket - * @param fd file descriptor of socket - */ - void (*process)(struct oonf_socket_entry *entry); - - /*! usage counter, will be increased every times the socket receives data */ - uint32_t _stat_recv; - - /*! usage counter, will be increased every times the socket sends data */ - uint32_t _stat_send; - - /*! - * usage counter, will be increased every times a socket processing takes - * more than a TIMER slice - */ - uint32_t _stat_long; - - /*! list of socket handlers */ - struct list_entity _node; -}; - EXPORT void oonf_socket_add(struct oonf_socket_entry *); EXPORT void oonf_socket_remove(struct oonf_socket_entry *); EXPORT void oonf_socket_set_read(struct oonf_socket_entry *entry, bool event_read); diff --git a/include/oonf/base/oonf_socket_data.h b/include/oonf/base/oonf_socket_data.h new file mode 100644 index 00000000..3fd2a9f7 --- /dev/null +++ b/include/oonf/base/oonf_socket_data.h @@ -0,0 +1,87 @@ + +/* + * The olsr.org Optimized Link-State Routing daemon version 2 (olsrd2) + * Copyright (c) 2004-2015, the olsr.org team - see HISTORY file + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of olsr.org, olsrd nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * Visit http://www.olsr.org for more information. + * + * If you find this software useful feel free to make a donation + * to the project. For more information see the website or contact + * the copyright holders. + * + */ + +/** + * @file + */ + +#ifndef OONF_SOCKET_DATA_H_ +#define OONF_SOCKET_DATA_H_ + +struct oonf_socket_entry; + +#include +#include +#include + +/** + * registered socket handler + */ +struct oonf_socket_entry { + /*! name of socket handler */ + const char *name; + + /*! file descriptor of the socket */ + struct os_fd fd; + + /** + * Callback when read or write event happens to socket + * @param fd file descriptor of socket + */ + void (*process)(struct oonf_socket_entry *entry); + + /*! usage counter, will be increased every times the socket receives data */ + uint32_t _stat_recv; + + /*! usage counter, will be increased every times the socket sends data */ + uint32_t _stat_send; + + /*! + * usage counter, will be increased every times a socket processing takes + * more than a TIMER slice + */ + uint32_t _stat_long; + + /*! list of socket handlers */ + struct list_entity _node; +}; + +#endif /* OONF_SOCKET_DATA_H_ */ diff --git a/include/oonf/base/oonf_stream_socket.h b/include/oonf/base/oonf_stream_socket.h index 044af6e2..4f5ecb04 100644 --- a/include/oonf/base/oonf_stream_socket.h +++ b/include/oonf/base/oonf_stream_socket.h @@ -46,8 +46,8 @@ #ifndef OONF_STREAM_SOCKET_H_ #define OONF_STREAM_SOCKET_H_ -#include #include +#include #include #include #include diff --git a/include/oonf/base/os_clock.h b/include/oonf/base/os_clock.h index 3a751578..e51af80e 100644 --- a/include/oonf/base/os_clock.h +++ b/include/oonf/base/os_clock.h @@ -56,11 +56,7 @@ #define OONF_OS_CLOCK_SUBSYSTEM "os_clock" #if defined(__linux__) -#include -#elif defined(BSD) -#include -#elif defined(_WIN32) -#include +#include #else #error "Unknown operation system" #endif @@ -69,4 +65,10 @@ static INLINE int os_clock_gettime64_ns(uint64_t *t64); static INLINE int os_clock_gettime64(uint64_t *t64); +#if defined(__linux__) +#include +#else +#error "Unknown operation system" +#endif + #endif /* OS_CLOCK_H_ */ diff --git a/include/oonf/base/os_fd.h b/include/oonf/base/os_fd.h index 8f74cf65..a0875fca 100644 --- a/include/oonf/base/os_fd.h +++ b/include/oonf/base/os_fd.h @@ -46,6 +46,12 @@ #ifndef OS_FD_H_ #define OS_FD_H_ +#if 0 +/* pre-definition of structs */ +struct os_fd; +struct os_fd_select; +#endif + #include #include @@ -57,13 +63,20 @@ #include #include +#include + +#if 0 +/* include os-specific headers */ +#if defined(__linux__) +#include +#elif defined(BSD) +#error "Unknown operation system" +#endif +#endif + /*! subsystem identifier */ #define OONF_OS_FD_SUBSYSTEM "os_fd" -/* pre-definition of structs */ -struct os_fd; -struct os_fd_select; - /* pre-declare inlines */ static INLINE int os_fd_init(struct os_fd *, int fd); static INLINE int os_fd_copy(struct os_fd *dst, struct os_fd *from); @@ -116,10 +129,6 @@ static INLINE uint8_t *os_fd_skip_rawsocket_prefix(uint8_t *ptr, ssize_t *len, i #if defined(__linux__) #include #elif defined(BSD) -#include -#elif defined(_WIN32) -#include -#else #error "Unknown operation system" #endif diff --git a/include/oonf/base/os_fd_data.h b/include/oonf/base/os_fd_data.h new file mode 100644 index 00000000..38234f15 --- /dev/null +++ b/include/oonf/base/os_fd_data.h @@ -0,0 +1,60 @@ + +/* + * The olsr.org Optimized Link-State Routing daemon version 2 (olsrd2) + * Copyright (c) 2004-2015, the olsr.org team - see HISTORY file + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of olsr.org, olsrd nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * Visit http://www.olsr.org for more information. + * + * If you find this software useful feel free to make a donation + * to the project. For more information see the website or contact + * the copyright holders. + * + */ + +/** + * @file + */ + +#ifndef OS_FD_DATA_H_ +#define OS_FD_DATA_H_ + +/* pre-definition of structs */ +struct os_fd; +struct os_fd_select; + +/* include os-specific headers */ +#if defined(__linux__) +#include +#elif defined(BSD) +#error "Unknown operation system" +#endif + +#endif /* OS_FD_DATA_H_ */ diff --git a/include/oonf/base/os_interface.h b/include/oonf/base/os_interface.h index a9671370..99f86c75 100644 --- a/include/oonf/base/os_interface.h +++ b/include/oonf/base/os_interface.h @@ -46,11 +46,19 @@ #ifndef OS_INTERFACE_H_ #define OS_INTERFACE_H_ +struct os_interface_ip_change; +struct os_interface_flags; +struct os_interface; +struct os_interface_ip; +struct os_interface_listener; + #include #include #include #include +#include +#include #include #include #include @@ -63,7 +71,7 @@ /* include os-specific headers */ #if defined(__linux__) -#include +#include #else #error "Unknown operation system" #endif @@ -238,13 +246,6 @@ struct os_interface_listener { struct list_entity _node; }; -/* include os-specific headers */ -#if defined(__linux__) -#include -#else -#error "Unknown operation system" -#endif - /* prototypes for all os_system functions */ static INLINE struct os_interface *os_interface_add(struct os_interface_listener *); static INLINE void os_interface_remove(struct os_interface_listener *); @@ -265,6 +266,13 @@ static INLINE const struct netaddr *os_interface_get_bindaddress( static INLINE const struct os_interface_ip *os_interface_get_prefix_from_dst( struct netaddr *destination, struct os_interface *ifdata); +/* include os-specific headers */ +#if defined(__linux__) +#include +#else +#error "Unknown operation system" +#endif + /** * @param ifname name of an interface * @return os interface instance, NULL if not registered diff --git a/include/oonf/base/os_linux/os_clock_linux.h b/include/oonf/base/os_linux/os_clock_linux.h index 5a69a102..5251e91e 100644 --- a/include/oonf/base/os_linux/os_clock_linux.h +++ b/include/oonf/base/os_linux/os_clock_linux.h @@ -47,6 +47,7 @@ #define OS_CLOCK_LINUX_H_ #include +#include EXPORT int os_clock_linux_gettime64_ns(uint64_t *t64); EXPORT int os_clock_linux_gettime64(uint64_t *t64); diff --git a/include/oonf/base/os_linux/os_clock_linux_data.h b/include/oonf/base/os_linux/os_clock_linux_data.h new file mode 100644 index 00000000..017eb967 --- /dev/null +++ b/include/oonf/base/os_linux/os_clock_linux_data.h @@ -0,0 +1,50 @@ + +/* + * The olsr.org Optimized Link-State Routing daemon version 2 (olsrd2) + * Copyright (c) 2004-2015, the olsr.org team - see HISTORY file + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of olsr.org, olsrd nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * Visit http://www.olsr.org for more information. + * + * If you find this software useful feel free to make a donation + * to the project. For more information see the website or contact + * the copyright holders. + * + */ + +/** + * @file + */ + +#ifndef OS_CLOCK_LINUX_DATA_H_ +#define OS_CLOCK_LINUX_DATA_H_ + + +#endif /* OS_CLOCK_LINUX_DATA_H_ */ diff --git a/include/oonf/base/os_linux/os_fd_linux.h b/include/oonf/base/os_linux/os_fd_linux.h index 8007c238..7a6cba84 100644 --- a/include/oonf/base/os_linux/os_fd_linux.h +++ b/include/oonf/base/os_linux/os_fd_linux.h @@ -52,7 +52,8 @@ #include #include -#include +#include +#include #include #include #include @@ -60,39 +61,6 @@ #include #include -/*! name of the loopback interface */ -#define IF_LOOPBACK_NAME "lo" - -enum os_fd_flags -{ - OS_FD_ACTIVE = 1, -}; - -/*! linux specific socket definition */ -struct os_fd { - /*! file descriptor of socket */ - int fd; - - /*! flags for telling epoll which events we are interested in */ - uint32_t wanted_events; - - /*! flags which were triggered in last epoll */ - uint32_t received_events; - - /*! flags for socket */ - enum os_fd_flags _flags; -}; - -/*! linux specific socket select definition */ -struct os_fd_select { - struct epoll_event _events[16]; - int _event_count; - - int _epoll_fd; - - uint64_t deadline; -}; - /** declare non-inline linux-specific functions */ EXPORT int os_fd_linux_event_wait(struct os_fd_select *); EXPORT int os_fd_linux_event_socket_modify(struct os_fd_select *sel, struct os_fd *sock); diff --git a/include/oonf/base/os_linux/os_fd_linux_data.h b/include/oonf/base/os_linux/os_fd_linux_data.h new file mode 100644 index 00000000..5b413c39 --- /dev/null +++ b/include/oonf/base/os_linux/os_fd_linux_data.h @@ -0,0 +1,84 @@ + +/* + * The olsr.org Optimized Link-State Routing daemon version 2 (olsrd2) + * Copyright (c) 2004-2015, the olsr.org team - see HISTORY file + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of olsr.org, olsrd nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * Visit http://www.olsr.org for more information. + * + * If you find this software useful feel free to make a donation + * to the project. For more information see the website or contact + * the copyright holders. + * + */ + +/** + * @file + */ + +#ifndef OS_FD_LINUX_DATA_H_ +#define OS_FD_LINUX_DATA_H_ + +#include + +/*! name of the loopback interface */ +#define IF_LOOPBACK_NAME "lo" + +enum os_fd_flags +{ + OS_FD_ACTIVE = 1, +}; + +/*! linux specific socket definition */ +struct os_fd { + /*! file descriptor of socket */ + int fd; + + /*! flags for telling epoll which events we are interested in */ + uint32_t wanted_events; + + /*! flags which were triggered in last epoll */ + uint32_t received_events; + + /*! flags for socket */ + enum os_fd_flags _flags; +}; + +/*! linux specific socket select definition */ +struct os_fd_select { + struct epoll_event _events[16]; + int _event_count; + + int _epoll_fd; + + uint64_t deadline; +}; + +#endif /* OS_FD_LINUX_DATA_H_ */ diff --git a/include/oonf/base/os_linux/os_interface_linux.h b/include/oonf/base/os_linux/os_interface_linux.h index b2408385..8850bd1b 100644 --- a/include/oonf/base/os_linux/os_interface_linux.h +++ b/include/oonf/base/os_linux/os_interface_linux.h @@ -52,14 +52,7 @@ #include #include #include - -enum -{ - /*! interval until an interface change trigger will be activated again */ - OS_INTERFACE_CHANGE_TRIGGER_INTERVAL = 200, -}; - -#define OS_INTERFACE_ANY "any" +#include EXPORT struct os_interface *os_interface_linux_add(struct os_interface_listener *); EXPORT void os_interface_linux_remove(struct os_interface_listener *); diff --git a/include/oonf/base/os_linux/os_interface_linux_internal.h b/include/oonf/base/os_linux/os_interface_linux_data.h similarity index 84% rename from include/oonf/base/os_linux/os_interface_linux_internal.h rename to include/oonf/base/os_linux/os_interface_linux_data.h index 174c2f2b..5ef411e6 100644 --- a/include/oonf/base/os_linux/os_interface_linux_internal.h +++ b/include/oonf/base/os_linux/os_interface_linux_data.h @@ -43,14 +43,22 @@ * @file */ -#ifndef OS_INTERFACE_LINUX_INTERNAL_H_ -#define OS_INTERFACE_LINUX_INTERNAL_H_ +#ifndef OS_INTERFACE_LINUX_DATA_H_ +#define OS_INTERFACE_LINUX_DATA_H_ -#include -#include +struct os_interface_address_change_internal; +struct os_interface_internal; #include -#include +#include + +enum +{ + /*! interval until an interface change trigger will be activated again */ + OS_INTERFACE_CHANGE_TRIGGER_INTERVAL = 200, +}; + +#define OS_INTERFACE_ANY "any" /** * define scope of address on interface @@ -70,8 +78,10 @@ struct os_interface_address_change_internal { /*! hook into list of IP address change handlers */ struct list_entity _node; - /*! netlink sequence number of command sent to the kernel */ - uint32_t nl_seq; + struct os_system_netlink_message msg; + + /* (well aligned) buffer for netlink message */ + uint64_t nl_buffer[256/sizeof(uint64_t)]; }; struct os_interface_internal { @@ -104,4 +114,4 @@ struct os_interface_internal { bool configured; }; -#endif /* OS_INTERFACE_LINUX_INTERNAL_H_ */ +#endif /* OS_INTERFACE_LINUX_DATA_H_ */ diff --git a/include/oonf/base/os_linux/os_routing_linux.h b/include/oonf/base/os_linux/os_routing_linux.h index 35d120ca..bad9207b 100644 --- a/include/oonf/base/os_linux/os_routing_linux.h +++ b/include/oonf/base/os_linux/os_routing_linux.h @@ -46,28 +46,11 @@ #ifndef OS_ROUTING_LINUX_H_ #define OS_ROUTING_LINUX_H_ -#include #include +#include #include - -/** - * linux specifc data for changing a kernel route - */ -struct os_route_internal { - /*! hook into list of route change handlers */ - struct avl_node _node; - - /*! netlink sequence number of command sent to the kernel */ - uint32_t nl_seq; -}; - -/** - * linux specific data for listening to kernel route changes - */ -struct os_route_listener_internal { - /*! hook into list of kernel route listeners */ - struct list_entity _node; -}; +#include +#include #include #include diff --git a/include/oonf/base/os_linux/os_routing_linux_data.h b/include/oonf/base/os_linux/os_routing_linux_data.h new file mode 100644 index 00000000..fc91cbd7 --- /dev/null +++ b/include/oonf/base/os_linux/os_routing_linux_data.h @@ -0,0 +1,78 @@ + +/* + * The olsr.org Optimized Link-State Routing daemon version 2 (olsrd2) + * Copyright (c) 2004-2015, the olsr.org team - see HISTORY file + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of olsr.org, olsrd nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * Visit http://www.olsr.org for more information. + * + * If you find this software useful feel free to make a donation + * to the project. For more information see the website or contact + * the copyright holders. + * + */ + +/** + * @file + */ + +#ifndef OS_ROUTING_LINUX_DATA_H_ +#define OS_ROUTING_LINUX_DATA_H_ + +struct os_route_internal; +struct os_route_listener_internal; + +#include +#include +#include +#include + +/** + * linux specifc data for changing a kernel route + */ +struct os_route_internal { + /*! hook into list of route change handlers */ + struct avl_node _node; + + struct os_system_netlink_message msg; + + /* (well aligned) buffer for netlink message */ + uint64_t nl_buffer[256/sizeof(uint64_t)]; +}; + +/** + * linux specific data for listening to kernel route changes + */ +struct os_route_listener_internal { + /*! hook into list of kernel route listeners */ + struct list_entity _node; +}; + +#endif /* OS_ROUTING_LINUX_DATA_H_ */ diff --git a/include/oonf/base/os_linux/os_system_linux.h b/include/oonf/base/os_linux/os_system_linux.h index 07eafe1d..d60f7aff 100644 --- a/include/oonf/base/os_linux/os_system_linux.h +++ b/include/oonf/base/os_linux/os_system_linux.h @@ -46,101 +46,29 @@ #ifndef OS_SYSTEM_LINUX_H_ #define OS_SYSTEM_LINUX_H_ +struct os_system_netlink_message; +struct os_system_netlink_socket; +struct os_system_netlink; + #include #include +#include #include #include #include #include - -/*! default timeout for netlink messages */ -#define OS_SYSTEM_NETLINK_TIMEOUT 1000 - -/** - * A buffer for transmitting netlink commands to the operation system - */ -struct os_system_netlink_buffer { - /*! hook into list of currently used buffers */ - struct list_entity _node; - - /*! total number of bytes in buffer */ - uint32_t total; - - /*! total number of messages in buffer */ - uint32_t messages; -}; - -/** - * Linux netlink handler - */ -struct os_system_netlink { - /*! name of netlink handler */ - const char *name; - - /*! socket handler for netlink communication */ - struct oonf_socket_entry socket; - - /*! output buffer for netlink data */ - struct autobuf out; - - /*! number of messages in output buffer */ - uint32_t out_messages; - - /*! link of data buffers to transmit */ - struct list_entity buffered; - - /*! subsystem that uses this netlink handler */ - struct oonf_subsystem *used_by; - - /*! pointer to currently processed netlink message header */ - struct nlmsghdr *in; - - /*! number of bytes of currently processed netlink message */ - size_t in_len; - - /*! number of messages in transit to the kernel */ - int msg_in_transit; - - /** - * Callback to handle incoming message from the kernel - * @param hdr netlink message header - */ - void (*cb_message)(struct nlmsghdr *hdr); - - /** - * Callback to handle error message of kernel - * @param seq netlink sequence number that triggered the error - * @param error error code - */ - void (*cb_error)(uint32_t seq, int error); - - /** - * Callback to notify the netlink communication timed out - */ - void (*cb_timeout)(void); - - /** - * Callback to notify that a netlink message has been processed - * @param seq sequence number of the processed netlink message - */ - void (*cb_done)(uint32_t seq); - - /*! netlink timeout handler */ - struct oonf_timer_instance timeout; -}; +#include EXPORT bool os_system_linux_is_ipv6_supported(void); EXPORT bool os_system_linux_is_minimal_kernel(int v1, int v2, int v3); EXPORT int os_system_linux_netlink_add(struct os_system_netlink *, int protocol); EXPORT void os_system_linux_netlink_remove(struct os_system_netlink *); -EXPORT int os_system_linux_netlink_send(struct os_system_netlink *fd, struct nlmsghdr *nl_hdr); -EXPORT int os_system_linux_netlink_add_mc(struct os_system_netlink *, const uint32_t *groups, size_t groupcount); -EXPORT int os_system_linux_netlink_drop_mc(struct os_system_netlink *, const int *groups, size_t groupcount); +EXPORT void os_system_linux_netlink_send(struct os_system_netlink *fd, struct os_system_netlink_message *msg); EXPORT int os_system_linux_netlink_addreq( - struct os_system_netlink *nl, struct nlmsghdr *n, int type, const void *data, int len); + struct os_system_netlink_message *nl_msg, int type, const void *data, int len); EXPORT int os_system_linux_linux_get_ioctl_fd(int af_type); @@ -154,16 +82,26 @@ os_system_is_ipv6_supported(void) { /** * Adds an address TLV to a netlink stream - * @param nl netlink handler - * @param n netlink message header + * @param nl_msg netlink message * @param type netlink TLV type * @param addr address * @return -1 if an error happened, 0 otherwise */ static INLINE int -os_system_linux_netlink_addnetaddr( - struct os_system_netlink *nl, struct nlmsghdr *n, int type, const struct netaddr *addr) { - return os_system_linux_netlink_addreq(nl, n, type, netaddr_get_binptr(addr), netaddr_get_maxprefix(addr) / 8); +os_system_linux_netlink_addnetaddr(struct os_system_netlink_message *nl_msg, int type, const struct netaddr *addr) { + return os_system_linux_netlink_addreq(nl_msg, type, netaddr_get_binptr(addr), netaddr_get_maxprefix(addr) / 8); +} + +static INLINE bool +os_system_linux_netlink_is_done(struct os_system_netlink_message *nl_msg) { + return !list_is_node_added(&nl_msg->_node); +} + +static INLINE void +os_system_linux_netlink_interrupt(struct os_system_netlink_message *nl_msg) { + if (list_is_node_added(&nl_msg->_node)) { + list_remove(&nl_msg->_node); + } } #endif /* OS_SYSTEM_LINUX_H_ */ diff --git a/include/oonf/base/os_linux/os_system_linux_data.h b/include/oonf/base/os_linux/os_system_linux_data.h new file mode 100644 index 00000000..06471c4d --- /dev/null +++ b/include/oonf/base/os_linux/os_system_linux_data.h @@ -0,0 +1,175 @@ + +/* + * The olsr.org Optimized Link-State Routing daemon version 2 (olsrd2) + * Copyright (c) 2004-2015, the olsr.org team - see HISTORY file + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of olsr.org, olsrd nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * Visit http://www.olsr.org for more information. + * + * If you find this software useful feel free to make a donation + * to the project. For more information see the website or contact + * the copyright holders. + * + */ + +/** + * @file + */ + +#ifndef OS_SYSTEM_LINUX_DATA_H_ +#define OS_SYSTEM_LINUX_DATA_H_ + +struct os_system_netlink_message; +struct os_system_netlink_socket; +struct os_system_netlink; + +#include +#include + +#include +#include +#include +#include +#include + +/*! default timeout for netlink messages */ +#define OS_SYSTEM_NETLINK_TIMEOUT 1000 + +/** + * Message for transfer to netlink subsystem + */ +struct os_system_netlink_message { + /*! pointer to buffer with netlink message */ + struct nlmsghdr *message; + + /*! maximum allowed length of netlink message */ + size_t max_length; + + /*! backlink to creater of message */ + struct os_system_netlink *originator; + + /*! error code received by message, 0 if okay, -1 if no response */ + int result; + + /*! true if this is a netlink tree dump */ + bool dump; + + /*! hook into list of messages, either buffered or sent */ + struct list_entity _node; +}; + +/** + * Centralized socket for all users of a certain netlink family type + */ +struct os_system_netlink_socket { + /*! NETLINK_xxx type socket */ + int32_t netlink_type; + + /*! list of netlink messages that should be sent to the socket */ + struct list_entity buffered_messages; + + /*! list of netlink messages that have been sent to the socket but not acked */ + struct list_entity sent_messages; + + /*! list of netlink socket handlers */ + struct list_entity handlers; + + /* netlink socket handler */ + struct oonf_socket_entry nl_socket; + + /* pid value of socket */ + uint32_t pid; + + /*! buffer for incoming netlink message processing */ + struct nlmsghdr *in; + + /*! maximum length of buffer for netlink message */ + size_t in_max_len; + + /*! netlink timeout handler */ + struct oonf_timer_instance timeout; + + /*! hook into tree of netlink socket */ + struct avl_node _node; +}; +/** + * Linux netlink handler + */ +struct os_system_netlink { + /*! name of netlink handler */ + const char *name; + + /* list of multicast groups this handler requires */ + const uint32_t *multicast; + + /*! number of multicast groups this handler requires */ + size_t multicast_count; + + /* reference to os system netlink multiplexer */ + struct os_system_netlink_socket *nl_socket; + + /*! subsystem that uses this netlink handler */ + struct oonf_subsystem *used_by; + + /** + * Callback to handle incoming message from the kernel + * @param nl pointer to this struct + * @param msg netlink message that triggered this received message + * @param hdr netlink message header received + */ + void (*cb_response)(struct os_system_netlink_message *msg, struct nlmsghdr *hdr); + + /** + * Callback to handle incoming message from the kernel + * @param nl pointer to this struct + * @param handler netlink hander that want to receive the multicast + * @param hdr netlink message header received + */ + void (*cb_multicast)(struct os_system_netlink *handler, struct nlmsghdr *hdr); + + /** + * Callback to handle error message of kernel + * @param nl pointer to this struct + * @param msg netlink message that was not sucessfully handled by kernel + */ + void (*cb_error)(struct os_system_netlink_message *msg); + + /** + * Callback to notify that a netlink message has been processed + * @param nl pointer to this struct + * @param msg netlink message that was sucessfully handled by kernel + */ + void (*cb_done)(struct os_system_netlink_message *msg); + + /*! hook into list of handlers for netlink protocol */ + struct list_entity _node; +}; +#endif /* OS_SYSTEM_LINUX_DATA_H_ */ + diff --git a/include/oonf/base/os_linux/os_tunnel_linux.h b/include/oonf/base/os_linux/os_tunnel_linux.h index c95f93ce..876a6516 100644 --- a/include/oonf/base/os_linux/os_tunnel_linux.h +++ b/include/oonf/base/os_linux/os_tunnel_linux.h @@ -48,8 +48,7 @@ #include #include - -struct os_tunnel_internal {}; +#include EXPORT int os_tunnel_linux_add(struct os_tunnel *tunnel); EXPORT int os_tunnel_linux_remove(struct os_tunnel *tunnel); diff --git a/include/oonf/base/os_linux/os_tunnel_linux_data.h b/include/oonf/base/os_linux/os_tunnel_linux_data.h new file mode 100644 index 00000000..385d2190 --- /dev/null +++ b/include/oonf/base/os_linux/os_tunnel_linux_data.h @@ -0,0 +1,52 @@ + +/* + * The olsr.org Optimized Link-State Routing daemon version 2 (olsrd2) + * Copyright (c) 2004-2015, the olsr.org team - see HISTORY file + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of olsr.org, olsrd nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * Visit http://www.olsr.org for more information. + * + * If you find this software useful feel free to make a donation + * to the project. For more information see the website or contact + * the copyright holders. + * + */ + +/** + * @file + */ + +#ifndef OS_TUNNEL_LINUX_DATA_H_ +#define OS_TUNNEL_LINUX_DATA_H_ + +struct os_tunnel_internal {}; + +#endif /* OS_TUNNEL_LINUX_DATA_H_ */ + diff --git a/include/oonf/base/os_linux/os_vif_linux.h b/include/oonf/base/os_linux/os_vif_linux.h index 1f941064..963b9025 100644 --- a/include/oonf/base/os_linux/os_vif_linux.h +++ b/include/oonf/base/os_linux/os_vif_linux.h @@ -48,13 +48,7 @@ #include #include - -/** - * Linux internal data for handling virtual interfaces (tun/tap) - */ -struct os_vif_internal { - /* no internal data necessary */ -}; +#include EXPORT int os_vif_linux_open(struct os_fd *fd, struct os_vif *vif); EXPORT void os_vif_linux_close(struct os_vif *vif); diff --git a/include/oonf/base/os_linux/os_vif_linux_data.h b/include/oonf/base/os_linux/os_vif_linux_data.h new file mode 100644 index 00000000..cc8304b8 --- /dev/null +++ b/include/oonf/base/os_linux/os_vif_linux_data.h @@ -0,0 +1,57 @@ + +/* + * The olsr.org Optimized Link-State Routing daemon version 2 (olsrd2) + * Copyright (c) 2004-2015, the olsr.org team - see HISTORY file + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of olsr.org, olsrd nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * Visit http://www.olsr.org for more information. + * + * If you find this software useful feel free to make a donation + * to the project. For more information see the website or contact + * the copyright holders. + * + */ + +/** + * @file + */ + +#ifndef OS_VIF_LINUX_DATA_H_ +#define OS_VIF_LINUX_DATA_H_ + +/** + * Linux internal data for handling virtual interfaces (tun/tap) + */ +struct os_vif_internal { + /* no internal data necessary */ +}; + +#endif /* OS_VIF_LINUX_DATA_H_ */ + diff --git a/include/oonf/base/os_routing.h b/include/oonf/base/os_routing.h index e4d89606..8cb08b7c 100644 --- a/include/oonf/base/os_routing.h +++ b/include/oonf/base/os_routing.h @@ -46,6 +46,12 @@ #ifndef OS_ROUTING_H_ #define OS_ROUTING_H_ +struct os_route; +struct os_route_listener; +struct os_route_str; +struct os_route_key; +struct os_route_listener; + #include #include @@ -58,13 +64,16 @@ #include #include +/* include os-specific headers */ +#if defined(__linux__) +#include +#else +#error "Unknown operation system" +#endif + /*! subsystem identifier */ #define OONF_OS_ROUTING_SUBSYSTEM "os_routing" -struct os_route; -struct os_route_listener; -struct os_route_str; - /* make sure default values for routing are there */ #ifndef RTPROT_UNSPEC /*! unspecified routing protocol */ @@ -115,6 +124,7 @@ enum os_route_type OS_ROUTE_UNICAST, OS_ROUTE_LOCAL, OS_ROUTE_BROADCAST, + OS_ROUTE_ANYCAST, OS_ROUTE_MULTICAST, OS_ROUTE_THROW, OS_ROUTE_UNREACHABLE, @@ -188,13 +198,6 @@ struct os_route_parameter { unsigned int if_index; }; -/* include os-specific headers */ -#if defined(__linux__) -#include -#else -#error "Unknown operation system" -#endif - /** * Handler for changing a route in the kernel * or querying the route status @@ -263,4 +266,11 @@ EXPORT int os_routing_avl_cmp_route_key(const void *, const void *); EXPORT const char *os_routing_cfg_get_rttype(size_t index, const void *unused); +/* include os-specific headers */ +#if defined(__linux__) +#include +#else +#error "Unknown operation system" +#endif + #endif /* OS_ROUTING_H_ */ diff --git a/include/oonf/base/os_system.h b/include/oonf/base/os_system.h index 71047311..9bdd34c5 100644 --- a/include/oonf/base/os_system.h +++ b/include/oonf/base/os_system.h @@ -46,9 +46,6 @@ #ifndef OS_SYSTEM_H_ #define OS_SYSTEM_H_ -#include -#include - #include /*! subsystem identifier */ @@ -56,15 +53,18 @@ /* include os-specific headers */ #if defined(__linux__) -#include -#elif defined(BSD) -#include -#elif defined(_WIN32) -#include +#include #else #error "Unknown operation system" #endif static INLINE bool os_system_is_ipv6_supported(void); +/* include os-specific headers */ +#if defined(__linux__) +#include +#else +#error "Unknown operation system" +#endif + #endif /* OS_SYSTEM_H_ */ diff --git a/include/oonf/base/os_tunnel.h b/include/oonf/base/os_tunnel.h index 1aad34a6..7941c823 100644 --- a/include/oonf/base/os_tunnel.h +++ b/include/oonf/base/os_tunnel.h @@ -46,6 +46,8 @@ #ifndef OS_TUNNEL_H_ #define OS_TUNNEL_H_ +struct os_tunnel; + #include #include #include @@ -53,11 +55,9 @@ /*! subsystem identifier */ #define OONF_OS_TUNNEL_SUBSYSTEM "os_tunnel" -struct os_tunnel; - /* include os-specific headers */ #if defined(__linux__) -#include +#include #else #error "Unknown operation system" #endif @@ -112,4 +112,11 @@ struct os_tunnel { static INLINE int os_tunnel_add(struct os_tunnel *); static INLINE int os_tunnel_remove(struct os_tunnel *); +/* include os-specific headers */ +#if defined(__linux__) +#include +#else +#error "Unknown operation system" +#endif + #endif /* OS_TUNNEL_H_ */ diff --git a/include/oonf/base/os_vif.h b/include/oonf/base/os_vif.h index 5d2cf1c8..6bb7ebba 100644 --- a/include/oonf/base/os_vif.h +++ b/include/oonf/base/os_vif.h @@ -46,6 +46,8 @@ #ifndef OS_VIF_H_ #define OS_VIF_H_ +struct os_vif; + #include #include @@ -58,7 +60,7 @@ struct os_vif; /* include os-specific headers */ #if defined(__linux__) -#include +#include #else #error "Unknown operation system" #endif @@ -121,4 +123,11 @@ os_vif_is_active(struct os_vif *vif) { return avl_is_node_added(&vif->_vif_node); } +/* include os-specific headers */ +#if defined(__linux__) +#include +#else +#error "Unknown operation system" +#endif + #endif /* OS_VIF_H_ */ diff --git a/include/oonf/generic/nl80211_listener/nl80211_get_interface.h b/include/oonf/generic/nl80211_listener/nl80211_get_interface.h index a6dc541d..f30c0cc7 100644 --- a/include/oonf/generic/nl80211_listener/nl80211_get_interface.h +++ b/include/oonf/generic/nl80211_listener/nl80211_get_interface.h @@ -51,7 +51,7 @@ #include void nl80211_send_get_interface( - struct os_system_netlink *nl, struct nlmsghdr *nl_msg, struct genlmsghdr *hdr, struct nl80211_if *interf); + struct os_system_netlink_message *nl_msg, struct genlmsghdr *hdr, struct nl80211_if *interf); void nl80211_process_get_interface_result(struct nl80211_if *interf, struct nlmsghdr *); #endif /* NL80211_GET_INTERFACE_H_ */ diff --git a/include/oonf/generic/nl80211_listener/nl80211_get_mpp.h b/include/oonf/generic/nl80211_listener/nl80211_get_mpp.h index aae83100..09b9a4f5 100644 --- a/include/oonf/generic/nl80211_listener/nl80211_get_mpp.h +++ b/include/oonf/generic/nl80211_listener/nl80211_get_mpp.h @@ -49,7 +49,7 @@ #include void nl80211_send_get_mpp( - struct os_system_netlink *nl, struct nlmsghdr *nl_msg, struct genlmsghdr *hdr, struct nl80211_if *interf); + struct os_system_netlink_message *nl_msg, struct genlmsghdr *hdr, struct nl80211_if *interf); void nl80211_process_get_mpp_result(struct nl80211_if *interf, struct nlmsghdr *); #endif /* NL80211_GET_MPP_H_ */ diff --git a/include/oonf/generic/nl80211_listener/nl80211_get_station_dump.h b/include/oonf/generic/nl80211_listener/nl80211_get_station_dump.h index 315cda04..fb858d61 100644 --- a/include/oonf/generic/nl80211_listener/nl80211_get_station_dump.h +++ b/include/oonf/generic/nl80211_listener/nl80211_get_station_dump.h @@ -49,7 +49,7 @@ #include void nl80211_send_get_station_dump( - struct os_system_netlink *nl, struct nlmsghdr *nl_msg, struct genlmsghdr *hdr, struct nl80211_if *interf); + struct os_system_netlink_message *nl_msg, struct genlmsghdr *hdr, struct nl80211_if *interf); void nl80211_process_get_station_dump_result(struct nl80211_if *interf, struct nlmsghdr *); #endif /* NL80211_GET_STATION_DUMP_H_ */ diff --git a/include/oonf/generic/nl80211_listener/nl80211_get_survey.h b/include/oonf/generic/nl80211_listener/nl80211_get_survey.h index c9a75480..dcb93c04 100644 --- a/include/oonf/generic/nl80211_listener/nl80211_get_survey.h +++ b/include/oonf/generic/nl80211_listener/nl80211_get_survey.h @@ -51,7 +51,7 @@ #include void nl80211_send_get_survey( - struct os_system_netlink *nl, struct nlmsghdr *nl_msg, struct genlmsghdr *hdr, struct nl80211_if *interf); + struct os_system_netlink_message *nl_msg, struct genlmsghdr *hdr, struct nl80211_if *interf); void nl80211_process_get_survey_result(struct nl80211_if *interf, struct nlmsghdr *); #endif /* NL80211_GET_SURVEY_H_ */ diff --git a/include/oonf/generic/nl80211_listener/nl80211_get_wiphy.h b/include/oonf/generic/nl80211_listener/nl80211_get_wiphy.h index 6343f7fb..5bce4e64 100644 --- a/include/oonf/generic/nl80211_listener/nl80211_get_wiphy.h +++ b/include/oonf/generic/nl80211_listener/nl80211_get_wiphy.h @@ -51,7 +51,7 @@ #include void nl80211_send_get_wiphy( - struct os_system_netlink *nl, struct nlmsghdr *nl_msg, struct genlmsghdr *hdr, struct nl80211_if *interf); + struct os_system_netlink_message *nl_msg, struct genlmsghdr *hdr, struct nl80211_if *interf); void nl80211_process_get_wiphy_result(struct nl80211_if *interf, struct nlmsghdr *hdr); void nl80211_finalize_get_wiphy(struct nl80211_if *interf); diff --git a/src/base/os_generic/os_routing_generic_rt_to_string.c b/src/base/os_generic/os_routing_generic_rt_to_string.c index dd8f6858..2bdd274c 100644 --- a/src/base/os_generic/os_routing_generic_rt_to_string.c +++ b/src/base/os_generic/os_routing_generic_rt_to_string.c @@ -51,6 +51,7 @@ static const char *_route_types[] = { [OS_ROUTE_UNICAST] = "unicast", [OS_ROUTE_LOCAL] = "local", [OS_ROUTE_BROADCAST] = "broadcast", + [OS_ROUTE_ANYCAST] = "anycast", [OS_ROUTE_MULTICAST] = "multicast", [OS_ROUTE_THROW] = "throw", [OS_ROUTE_UNREACHABLE] = "unreachable", diff --git a/src/base/os_linux/os_interface_linux.c b/src/base/os_linux/os_interface_linux.c index 5cf6a93d..267bdb4a 100644 --- a/src/base/os_linux/os_interface_linux.c +++ b/src/base/os_linux/os_interface_linux.c @@ -113,17 +113,9 @@ static void _init_mesh(struct os_interface *os_if); static void _refresh_mesh(struct os_interface *os_if, char *old_redirect, char *old_spoof); static void _cleanup_mesh(struct os_interface *os_if); -static void _query_interface_links(void); -static void _query_interface_addresses(void); - -static void _cb_rtnetlink_message(struct nlmsghdr *hdr); -static void _cb_rtnetlink_error(uint32_t seq, int error); -static void _cb_rtnetlink_done(uint32_t seq); -static void _cb_rtnetlink_timeout(void); -static void _cb_query_error(uint32_t seq, int error); -static void _cb_query_done(uint32_t seq); -static void _cb_query_timeout(void); -static void _address_finished(struct os_interface_ip_change *addr, int error); +static void _cb_rtnetlink_response(struct os_system_netlink_message *nl_msg, struct nlmsghdr *hdr); +static void _cb_rtnetlink_multicast(struct os_system_netlink *nl, struct nlmsghdr *hdr); +static void _cb_rtnetlink_feedback(struct os_system_netlink_message *nl_msg); static void _activate_if_routing(void); static void _deactivate_if_routing(void); @@ -163,33 +155,31 @@ static struct oonf_subsystem _oonf_os_interface_subsystem = { }; DECLARE_OONF_PLUGIN(_oonf_os_interface_subsystem); -/* rtnetlink receiver for interface and address events */ -static struct os_system_netlink _rtnetlink_receiver = { - .name = "interface snooper", - .used_by = &_oonf_os_interface_subsystem, - .cb_message = _cb_rtnetlink_message, - .cb_error = _cb_rtnetlink_error, - .cb_done = _cb_rtnetlink_done, - .cb_timeout = _cb_rtnetlink_timeout, -}; - -static struct list_entity _rtnetlink_feedback; - static const uint32_t _rtnetlink_mcast[] = { RTNLGRP_LINK, RTNLGRP_IPV4_IFADDR, RTNLGRP_IPV6_IFADDR }; -static struct os_system_netlink _rtnetlink_if_query = { - .name = "interface query", +static struct os_system_netlink _rtnetlink_handler = { + .name = "interface", + .multicast = _rtnetlink_mcast, + .multicast_count = ARRAYSIZE(_rtnetlink_mcast), + .used_by = &_oonf_os_interface_subsystem, - .cb_message = _cb_rtnetlink_message, - .cb_error = _cb_query_error, - .cb_done = _cb_query_done, - .cb_timeout = _cb_query_timeout, + .cb_response = _cb_rtnetlink_response, + .cb_multicast = _cb_rtnetlink_multicast, + .cb_error = _cb_rtnetlink_feedback, + .cb_done = _cb_rtnetlink_feedback, }; -static bool _link_query_in_progress = false; -static bool _address_query_in_progress = false; -static bool _trigger_link_query = false; -static bool _trigger_address_query = false; +static uint8_t nl_buffer1[256], nl_buffer2[256]; +static struct os_system_netlink_message _nl_link_query = { + .message = (struct nlmsghdr*)nl_buffer1, + .max_length = ARRAYSIZE(nl_buffer1), + .originator = &_rtnetlink_handler, +}; +static struct os_system_netlink_message _nl_address_query = { + .message = (struct nlmsghdr*)nl_buffer2, + .max_length = ARRAYSIZE(nl_buffer2), + .originator = &_rtnetlink_handler, +}; /* global procfile state before initialization */ static char _original_rp_filter; @@ -233,22 +223,14 @@ static const char _ANY_INTERFACE[] = OS_INTERFACE_ANY; */ static int _init(void) { - if (os_system_linux_netlink_add(&_rtnetlink_receiver, NETLINK_ROUTE)) { - return -1; - } - - if (os_system_linux_netlink_add(&_rtnetlink_if_query, NETLINK_ROUTE)) { - os_system_linux_netlink_remove(&_rtnetlink_receiver); - return -1; - } + struct nlmsghdr *msg; + struct ifinfomsg *ifi; + struct ifaddrmsg *ifa; - if (os_system_linux_netlink_add_mc(&_rtnetlink_receiver, _rtnetlink_mcast, ARRAYSIZE(_rtnetlink_mcast))) { - os_system_linux_netlink_remove(&_rtnetlink_receiver); - os_system_linux_netlink_remove(&_rtnetlink_if_query); + if (os_system_linux_netlink_add(&_rtnetlink_handler, NETLINK_ROUTE)) { return -1; } - list_init_head(&_rtnetlink_feedback); avl_init(&_interface_data_tree, avl_comp_strcasecmp, false); oonf_class_add(&_interface_data_class); oonf_class_add(&_interface_ip_class); @@ -257,6 +239,34 @@ _init(void) { _is_kernel_2_6_31_or_better = os_system_linux_is_minimal_kernel(2, 6, 31); + /* init link query */ + msg = (void *)&nl_buffer1[0]; + + /* get link level data */ + memset(nl_buffer1, 0, sizeof(nl_buffer1)); + msg->nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP; + msg->nlmsg_type = RTM_GETLINK; + + /* set length of netlink message with ifinfomsg payload */ + msg->nlmsg_len = NLMSG_LENGTH(sizeof(*ifi)); + + ifi = NLMSG_DATA(msg); + ifi->ifi_family = AF_NETLINK; + + /* init address query */ + msg = (void *)&nl_buffer2[0]; + + /* get IP level data */ + memset(nl_buffer2, 0, sizeof(nl_buffer2)); + msg->nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP; + msg->nlmsg_type = RTM_GETADDR; + + /* set length of netlink message with ifaddrmsg payload */ + msg->nlmsg_len = NLMSG_LENGTH(sizeof(*ifa)); + + ifa = NLMSG_DATA(msg); + ifa->ifa_family = AF_UNSPEC; + return 0; } @@ -286,8 +296,7 @@ _cleanup(void) { oonf_class_remove(&_interface_data_class); oonf_class_remove(&_interface_class); - os_system_linux_netlink_remove(&_rtnetlink_if_query); - os_system_linux_netlink_remove(&_rtnetlink_receiver); + os_system_linux_netlink_remove(&_rtnetlink_handler); } /** @@ -437,19 +446,18 @@ os_interface_linux_state_set(struct os_interface *os_if, bool up) { */ int os_interface_linux_address_set(struct os_interface_ip_change *addr) { - uint8_t buffer[UIO_MAXIOV]; - struct nlmsghdr *msg; struct ifaddrmsg *ifaddrreq; - int seq; + struct nlmsghdr *msg; #if defined(OONF_LOG_DEBUG_INFO) struct netaddr_str nbuf; #endif - memset(buffer, 0, sizeof(buffer)); - /* get pointers for netlink message */ - msg = (void *)&buffer[0]; - + addr->_internal.msg.message = (void *)addr->_internal.nl_buffer; + addr->_internal.msg.max_length = sizeof(addr->_internal.nl_buffer); + msg = addr->_internal.msg.message; + + memset(msg, 0, addr->_internal.msg.max_length); if (addr->set) { msg->nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE | NLM_F_REPLACE | NLM_F_ACK; msg->nlmsg_type = RTM_NEWADDR; @@ -471,17 +479,12 @@ os_interface_linux_address_set(struct os_interface_ip_change *addr) { ifaddrreq->ifa_index = addr->if_index; ifaddrreq->ifa_scope = addr->scope; - if (os_system_linux_netlink_addnetaddr(&_rtnetlink_receiver, msg, IFA_LOCAL, &addr->address)) { + if (os_system_linux_netlink_addnetaddr(&addr->_internal.msg, IFA_LOCAL, &addr->address)) { return -1; } /* cannot fail */ - seq = os_system_linux_netlink_send(&_rtnetlink_receiver, msg); - - if (addr->cb_finished) { - list_add_tail(&_rtnetlink_feedback, &addr->_internal._node); - addr->_internal.nl_seq = seq; - } + os_system_linux_netlink_send(&_rtnetlink_handler, &addr->_internal.msg); return 0; } @@ -566,15 +569,17 @@ _add_interface(const char *name) { data->flags.up = true; } - /* trigger new queries */ - _trigger_link_query = true; - _trigger_address_query = true; - data->if_linklocal_v4 = &NETADDR_UNSPEC; data->if_linklocal_v6 = &NETADDR_UNSPEC; data->if_v4 = &NETADDR_UNSPEC; data->if_v6 = &NETADDR_UNSPEC; - _query_interface_links(); + + if (os_system_linux_netlink_is_done(&_nl_address_query)) { + os_system_linux_netlink_send(&_rtnetlink_handler, &_nl_address_query); + } + if (os_system_linux_netlink_is_done(&_nl_link_query)) { + os_system_linux_netlink_send(&_rtnetlink_handler, &_nl_link_query); + } } return data; @@ -829,80 +834,6 @@ _os_linux_writeToFile(const char *file, char *old, char value) { return 0; } -/** - * Query a dump of all interface link data - */ -static void -_query_interface_links(void) { - uint8_t buffer[UIO_MAXIOV]; - struct nlmsghdr *msg; - struct ifinfomsg *ifi; -#if defined(OONF_LOG_DEBUG_INFO) -#endif - - if (_link_query_in_progress || _address_query_in_progress) { - return; - } - - OONF_DEBUG(LOG_OS_INTERFACE, "Request all interface links"); - - _trigger_link_query = false; - _link_query_in_progress = true; - - /* get pointers for netlink message */ - msg = (void *)&buffer[0]; - - /* get link level data */ - memset(buffer, 0, sizeof(buffer)); - msg->nlmsg_flags = NLM_F_REQUEST | NLM_F_ROOT; - msg->nlmsg_type = RTM_GETLINK; - - /* set length of netlink message with ifinfomsg payload */ - msg->nlmsg_len = NLMSG_LENGTH(sizeof(*ifi)); - - ifi = NLMSG_DATA(msg); - ifi->ifi_family = AF_NETLINK; - - /* we don't care for the sequence number */ - os_system_linux_netlink_send(&_rtnetlink_if_query, msg); -} - -/** - * Query a dump of all interface link data - */ -static void -_query_interface_addresses(void) { - uint8_t buffer[UIO_MAXIOV]; - struct nlmsghdr *msg; - struct ifaddrmsg *ifa; - - if (_link_query_in_progress || _address_query_in_progress) { - return; - } - - _trigger_address_query = false; - _address_query_in_progress = true; - - OONF_DEBUG(LOG_OS_INTERFACE, "Request all interface addresses"); - - /* get pointers for netlink message */ - msg = (void *)&buffer[0]; - - /* get IP level data */ - memset(buffer, 0, sizeof(buffer)); - msg->nlmsg_flags = NLM_F_REQUEST | NLM_F_ROOT; - msg->nlmsg_type = RTM_GETADDR; - - /* set length of netlink message with ifaddrmsg payload */ - msg->nlmsg_len = NLMSG_LENGTH(sizeof(*ifa)); - - ifa = NLMSG_DATA(msg); - ifa->ifa_family = AF_UNSPEC; - - /* we don't care for the sequence number */ - os_system_linux_netlink_send(&_rtnetlink_if_query, msg); -} - /** * Trigger all change listeners of a network interface * @param os_if network interface @@ -1211,12 +1142,17 @@ _address_parse_nlmsg(const char *ifname, struct nlmsghdr *msg) { } } +static void +_cb_rtnetlink_response(struct os_system_netlink_message *nl_msg, struct nlmsghdr *hdr) { + _cb_rtnetlink_multicast(nl_msg->originator, hdr); +} + /** * Handle incoming rtnetlink multicast messages for interface listeners * @param hdr pointer to netlink message */ static void -_cb_rtnetlink_message(struct nlmsghdr *hdr) { +_cb_rtnetlink_multicast(struct os_system_netlink *nl __attribute__((unused)), struct nlmsghdr *hdr) { struct ifinfomsg *ifi; struct ifaddrmsg *ifa; char ifname[IF_NAMESIZE]; @@ -1241,7 +1177,7 @@ _cb_rtnetlink_message(struct nlmsghdr *hdr) { _address_parse_nlmsg(ifname, hdr); } else { - OONF_DEBUG(LOG_OS_INTERFACE, "Message type: %u", hdr->nlmsg_type); + OONF_WARN(LOG_OS_INTERFACE, "Message type: %u", hdr->nlmsg_type); } } @@ -1251,140 +1187,25 @@ _cb_rtnetlink_message(struct nlmsghdr *hdr) { * @param error error code */ static void -_cb_rtnetlink_error(uint32_t seq, int error) { +_cb_rtnetlink_feedback(struct os_system_netlink_message *nl_msg) { struct os_interface_ip_change *addr; - OONF_INFO(LOG_OS_INTERFACE, "Netlink socket provided feedback: %d %d", seq, error); - - /* transform into errno number */ - list_for_each_element(&_rtnetlink_feedback, addr, _internal._node) { - if (seq == addr->_internal.nl_seq) { - _address_finished(addr, error); - break; - } - } -} - -/** - * Handle ack timeout from netlink socket - */ -static void -_cb_rtnetlink_timeout(void) { - struct os_interface_ip_change *addr; - - OONF_INFO(LOG_OS_INTERFACE, "Netlink socket timed out"); - - list_for_each_element(&_rtnetlink_feedback, addr, _internal._node) { - _address_finished(addr, -1); - } -} - -/** - * Handle done from multipart netlink messages - * @param seq sequence number of netlink message - */ -static void -_cb_rtnetlink_done(uint32_t seq) { - struct os_interface_ip_change *addr; - - OONF_INFO(LOG_OS_INTERFACE, "Netlink operation finished: %u", seq); - - list_for_each_element(&_rtnetlink_feedback, addr, _internal._node) { - if (seq == addr->_internal.nl_seq) { - _address_finished(addr, 0); - break; - } + OONF_INFO(LOG_OS_INTERFACE, "Netlink socket provided feedback: %d %d", + nl_msg->message->nlmsg_seq, nl_msg->result); + if (nl_msg->dump) { + /* this was one of the queries, no need to react */ + return; } -} -/** - * Stop processing of an ip address command and set error code - * for callback - * @param addr pointer to os_system_address - * @param error error code, 0 if no error - */ -static void -_address_finished(struct os_interface_ip_change *addr, int error) { + addr = container_of(nl_msg, struct os_interface_ip_change, _internal.msg); if (list_is_node_added(&addr->_internal._node)) { /* remove first to prevent any kind of recursive cleanup */ list_remove(&addr->_internal._node); if (addr->cb_finished) { - addr->cb_finished(addr, error); - } - } -} - -/** - * Handle switching between netlink query for links and addresses - */ -static void -_process_end_of_query(void) { - if (_link_query_in_progress) { - _link_query_in_progress = false; - - if (_trigger_address_query) { - _query_interface_addresses(); - } - else if (_trigger_link_query) { - _query_interface_links(); + addr->cb_finished(addr, nl_msg->result); } } - else { - _address_query_in_progress = false; - - if (_trigger_link_query) { - _query_interface_links(); - } - else if (_trigger_address_query) { - _query_interface_addresses(); - } - } -} - -/** - * Handle a netlink query that did not work out - */ -static void -_process_bad_end_of_query(void) { - /* reactivate query that has failed */ - if (_link_query_in_progress) { - _trigger_link_query = true; - } - if (_address_query_in_progress) { - _trigger_address_query = true; - } - _process_end_of_query(); -} - -/** - * Handle an incoming netlink query error - * @param seq sequence number of netlink message - * @param error error code - */ -static void -_cb_query_error(uint32_t seq __attribute((unused)), int error __attribute((unused))) { - OONF_DEBUG(LOG_OS_INTERFACE, "Received error %d for query %u", error, seq); - _process_bad_end_of_query(); -} - -/** - * Handle a successful netlink query - * @param seq sequence number of netlink message - */ -static void -_cb_query_done(uint32_t seq __attribute((unused))) { - OONF_DEBUG(LOG_OS_INTERFACE, "Query %u done", seq); - _process_end_of_query(); -} - -/** - * Handle a timeout of a netlink query - */ -static void -_cb_query_timeout(void) { - OONF_DEBUG(LOG_OS_INTERFACE, "Query timeout"); - _process_bad_end_of_query(); } /** diff --git a/src/base/os_linux/os_routing_linux.c b/src/base/os_linux/os_routing_linux.c index 10dbe35f..286a5b18 100644 --- a/src/base/os_linux/os_routing_linux.c +++ b/src/base/os_linux/os_routing_linux.c @@ -51,9 +51,9 @@ #include #include +#include #include #include -#include #include #include @@ -78,13 +78,13 @@ struct route_type_translation { static int _init(void); static void _cleanup(void); -static int _routing_set(struct nlmsghdr *msg, struct os_route *route, unsigned char rt_scope); +static int _routing_set(struct os_system_netlink_message *msg, struct os_route *route, unsigned char rt_scope); static void _routing_finished(struct os_route *route, int error); -static void _cb_rtnetlink_message(struct nlmsghdr *); -static void _cb_rtnetlink_error(uint32_t seq, int err); -static void _cb_rtnetlink_done(uint32_t seq); -static void _cb_rtnetlink_timeout(void); +static void _cb_rtnetlink_response(struct os_system_netlink_message *msg, struct nlmsghdr *header); +static void _cb_rtnetlink_multicast(struct os_system_netlink *nl, struct nlmsghdr *header); +static void _cb_rtnetlink_error(struct os_system_netlink_message *nl_msg); +static void _cb_rtnetlink_done(struct os_system_netlink_message *nl_msg); /* subsystem definition */ static const char *_dependencies[] = { @@ -102,7 +102,9 @@ DECLARE_OONF_PLUGIN(_oonf_os_routing_subsystem); /* translation table between route types */ static struct route_type_translation _type_translation[] = { { OS_ROUTE_UNICAST, RTN_UNICAST }, - { OS_ROUTE_LOCAL, RTN_LOCAL }, { OS_ROUTE_BROADCAST, RTN_BROADCAST }, { OS_ROUTE_MULTICAST, RTN_MULTICAST }, + { OS_ROUTE_LOCAL, RTN_LOCAL }, { OS_ROUTE_BROADCAST, RTN_BROADCAST }, + { OS_ROUTE_ANYCAST, RTN_ANYCAST }, + { OS_ROUTE_MULTICAST, RTN_MULTICAST }, { OS_ROUTE_THROW, RTN_THROW }, { OS_ROUTE_UNREACHABLE, RTN_UNREACHABLE }, { OS_ROUTE_PROHIBIT, RTN_PROHIBIT }, { OS_ROUTE_BLACKHOLE, RTN_BLACKHOLE }, { OS_ROUTE_NAT, RTN_NAT } }; @@ -110,15 +112,18 @@ static struct route_type_translation _type_translation[] = { { OS_ROUTE_UNICAST, static const uint32_t _rtnetlink_mcast[] = { RTNLGRP_IPV4_ROUTE, RTNLGRP_IPV6_ROUTE }; static struct os_system_netlink _rtnetlink_socket = { - .name = "routing", + .name = "routing send", .used_by = &_oonf_os_routing_subsystem, - .cb_message = _cb_rtnetlink_message, + + .multicast = &_rtnetlink_mcast[0], + .multicast_count = ARRAYSIZE(_rtnetlink_mcast), + + .cb_response = _cb_rtnetlink_response, + .cb_multicast = _cb_rtnetlink_multicast, .cb_error = _cb_rtnetlink_error, .cb_done = _cb_rtnetlink_done, - .cb_timeout = _cb_rtnetlink_timeout, }; -static struct avl_tree _rtnetlink_feedback; static struct list_entity _rtnetlink_listener; /* default wildcard route */ @@ -148,11 +153,7 @@ _init(void) { if (os_system_linux_netlink_add(&_rtnetlink_socket, NETLINK_ROUTE)) { return -1; } - if (os_system_linux_netlink_add_mc(&_rtnetlink_socket, _rtnetlink_mcast, ARRAYSIZE(_rtnetlink_mcast))) { - os_system_linux_netlink_remove(&_rtnetlink_socket); - return -1; - } - avl_init(&_rtnetlink_feedback, avl_comp_uint32, false); + list_init_head(&_rtnetlink_listener); _is_kernel_3_11_0_or_better = os_system_linux_is_minimal_kernel(3, 11, 0); @@ -164,11 +165,7 @@ _init(void) { */ static void _cleanup(void) { - struct os_route *rt, *rt_it; - - avl_for_each_element_safe(&_rtnetlink_feedback, rt, _internal._node, rt_it) { - _routing_finished(rt, 1); - } + /* TODO: remove out netlink handler */ os_system_linux_netlink_remove(&_rtnetlink_socket); } @@ -201,21 +198,24 @@ os_routing_linux_supports_source_specific(int af_family) { */ int os_routing_linux_set(struct os_route *route, bool set, bool del_similar) { - uint8_t buffer[UIO_MAXIOV]; - struct nlmsghdr *msg; - unsigned char scope; struct os_route os_rt; - int seq; + unsigned char scope; + struct nlmsghdr *msg; +#ifdef OONF_LOG_DEBUG_INFO struct os_route_str rbuf; +#endif - memset(buffer, 0, sizeof(buffer)); + /* clear netlink buffer */ + msg = (struct nlmsghdr *)&route->_internal.nl_buffer[0]; + memset(msg, 0, sizeof(route->_internal.nl_buffer)); + + /* get pointers for netlink message */ + route->_internal.msg.message = msg; + route->_internal.msg.max_length = sizeof(route->_internal.nl_buffer); /* copy route settings */ memcpy(&os_rt, route, sizeof(os_rt)); - /* get pointers for netlink message */ - msg = (void *)&buffer[0]; - msg->nlmsg_flags = NLM_F_REQUEST; /* set length of netlink message with rtmsg payload */ @@ -251,22 +251,13 @@ os_routing_linux_set(struct os_route *route, bool set, bool del_similar) { OONF_DEBUG(LOG_OS_ROUTING, "%sset route: %s", set ? "" : "re", os_routing_to_string(&rbuf, &os_rt.p)); - if (_routing_set(msg, &os_rt, scope)) { + if (_routing_set(&route->_internal.msg, &os_rt, scope)) { + OONF_WARN(LOG_OS_ROUTING, "%sset route failed", set ? "": "re"); return -1; } /* cannot fail */ - seq = os_system_linux_netlink_send(&_rtnetlink_socket, msg); - - if (route->cb_finished) { - route->_internal.nl_seq = seq; - route->_internal._node.key = &route->_internal.nl_seq; - - OONF_ASSERT(!avl_is_node_added(&route->_internal._node), - LOG_OS_ROUTING, "route %s is already in feedback list!", - os_routing_to_string(&rbuf, &os_rt.p)); - avl_insert(&_rtnetlink_feedback, &route->_internal._node); - } + os_system_linux_netlink_send(&_rtnetlink_socket, &route->_internal.msg); return 0; } @@ -277,16 +268,23 @@ os_routing_linux_set(struct os_route *route, bool set, bool del_similar) { */ int os_routing_linux_query(struct os_route *route) { - uint8_t buffer[UIO_MAXIOV]; struct nlmsghdr *msg; struct rtgenmsg *rt_gen; - int seq; - +#ifdef OONF_LOG_DEBUG_INFO + struct os_route_str rbuf; +#endif OONF_ASSERT(route->cb_finished != NULL && route->cb_get != NULL, LOG_OS_ROUTING, "illegal route query"); - memset(buffer, 0, sizeof(buffer)); + OONF_DEBUG(LOG_OS_ROUTING, "routing query: %s", os_routing_to_string(&rbuf, &route->p)); + + /* clear netlink buffer */ + msg = (struct nlmsghdr *)&route->_internal.nl_buffer[0]; + memset(msg, 0, sizeof(route->_internal.nl_buffer)); + + /* get pointers for netlink message */ + route->_internal.msg.message = msg; + route->_internal.msg.max_length = sizeof(route->_internal.nl_buffer); /* get pointers for netlink message */ - msg = (void *)&buffer[0]; rt_gen = NLMSG_DATA(msg); msg->nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP; @@ -297,14 +295,7 @@ os_routing_linux_query(struct os_route *route) { msg->nlmsg_type = RTM_GETROUTE; rt_gen->rtgen_family = route->p.family; - seq = os_system_linux_netlink_send(&_rtnetlink_socket, msg); - if (seq < 0) { - return -1; - } - - route->_internal.nl_seq = seq; - route->_internal._node.key = &route->_internal.nl_seq; - avl_insert(&_rtnetlink_feedback, &route->_internal._node); + os_system_linux_netlink_send(&_rtnetlink_socket, &route->_internal.msg); return 0; } @@ -367,8 +358,8 @@ os_routing_linux_init_wildcard_route(struct os_route *route) { static void _routing_finished(struct os_route *route, int error) { /* remove first to prevent any kind of recursive cleanup */ - avl_remove(&_rtnetlink_feedback, &route->_internal._node); - + os_system_linux_netlink_interrupt(&route->_internal.msg); + if (route->cb_finished) { route->cb_finished(route, error); } @@ -382,10 +373,13 @@ _routing_finished(struct os_route *route, int error) { * @return -1 if an error happened, 0 otherwise */ static int -_routing_set(struct nlmsghdr *msg, struct os_route *route, unsigned char rt_scope) { +_routing_set(struct os_system_netlink_message *nl_msg, struct os_route *route, unsigned char rt_scope) { struct rtmsg *rt_msg; + struct nlmsghdr *msg; size_t i; + msg = nl_msg->message; + /* calculate address af_type */ if (netaddr_get_address_family(&route->p.key.dst) != AF_UNSPEC) { route->p.family = netaddr_get_address_family(&route->p.key.dst); @@ -429,7 +423,7 @@ _routing_set(struct nlmsghdr *msg, struct os_route *route, unsigned char rt_scop /* add attributes */ if (netaddr_get_address_family(&route->p.src_ip) != AF_UNSPEC) { /* add src-ip */ - if (os_system_linux_netlink_addnetaddr(&_rtnetlink_socket, msg, RTA_PREFSRC, &route->p.src_ip)) { + if (os_system_linux_netlink_addnetaddr(nl_msg, RTA_PREFSRC, &route->p.src_ip)) { return -1; } } @@ -438,7 +432,7 @@ _routing_set(struct nlmsghdr *msg, struct os_route *route, unsigned char rt_scop rt_msg->rtm_flags |= RTNH_F_ONLINK; /* add gateway */ - if (os_system_linux_netlink_addnetaddr(&_rtnetlink_socket, msg, RTA_GATEWAY, &route->p.gw)) { + if (os_system_linux_netlink_addnetaddr(nl_msg, RTA_GATEWAY, &route->p.gw)) { return -1; } } @@ -447,7 +441,7 @@ _routing_set(struct nlmsghdr *msg, struct os_route *route, unsigned char rt_scop rt_msg->rtm_dst_len = netaddr_get_prefix_length(&route->p.key.dst); /* add destination */ - if (os_system_linux_netlink_addnetaddr(&_rtnetlink_socket, msg, RTA_DST, &route->p.key.dst)) { + if (os_system_linux_netlink_addnetaddr(nl_msg, RTA_DST, &route->p.key.dst)) { return -1; } } @@ -456,23 +450,21 @@ _routing_set(struct nlmsghdr *msg, struct os_route *route, unsigned char rt_scop rt_msg->rtm_src_len = netaddr_get_prefix_length(&route->p.key.src); /* add source-specific routing prefix */ - if (os_system_linux_netlink_addnetaddr(&_rtnetlink_socket, msg, RTA_SRC, &route->p.key.src)) { + if (os_system_linux_netlink_addnetaddr(nl_msg, RTA_SRC, &route->p.key.src)) { return -1; } } if (route->p.metric != -1) { /* add metric */ - if (os_system_linux_netlink_addreq( - &_rtnetlink_socket, msg, RTA_PRIORITY, &route->p.metric, sizeof(route->p.metric))) { + if (os_system_linux_netlink_addreq(nl_msg, RTA_PRIORITY, &route->p.metric, sizeof(route->p.metric))) { return -1; } } if (route->p.if_index) { /* add interface*/ - if (os_system_linux_netlink_addreq( - &_rtnetlink_socket, msg, RTA_OIF, &route->p.if_index, sizeof(route->p.if_index))) { + if (os_system_linux_netlink_addreq(nl_msg, RTA_OIF, &route->p.if_index, sizeof(route->p.if_index))) { return -1; } } @@ -523,7 +515,7 @@ _routing_parse_nlmsg(struct os_route *route, struct nlmsghdr *msg) { } } if (route->p.type == OS_ROUTE_UNDEFINED) { - OONF_DEBUG(LOG_OS_ROUTING, "Got route type: %u", rt_msg->rtm_type); + OONF_DEBUG(LOG_OS_ROUTING, "Got route type %u which has no OONF match", rt_msg->rtm_type); return 2; } @@ -609,90 +601,94 @@ _match_routes(struct os_route *filter, struct os_route *route) { * @param msg netlink message including header */ static void -_cb_rtnetlink_message(struct nlmsghdr *msg) { +_cb_rtnetlink_multicast(struct os_system_netlink *nl __attribute__((unused)), struct nlmsghdr *msg) { struct os_route_listener *listener; - struct os_route *filter; - struct os_route rt; + struct os_route netlink_payload; int result; #ifdef OONF_LOG_DEBUG_INFO struct os_route_str rbuf; #endif - OONF_DEBUG(LOG_OS_ROUTING, "Got message: %d %d 0x%04x", msg->nlmsg_seq, msg->nlmsg_type, msg->nlmsg_flags); if (msg->nlmsg_type != RTM_NEWROUTE && msg->nlmsg_type != RTM_DELROUTE) { + OONF_DEBUG(LOG_OS_ROUTING, "'Received (unnecessary) netlink message type %u", msg->nlmsg_type); return; } - if ((result = _routing_parse_nlmsg(&rt, msg))) { - if (result < 0) { - OONF_WARN(LOG_OS_ROUTING, "Error while processing route reply (result %d)", result); - } + result = _routing_parse_nlmsg(&netlink_payload, msg); + if (result < 0) { + OONF_WARN(LOG_OS_ROUTING, "Error while processing route reply (result %d)", result); return; } - - OONF_DEBUG(LOG_OS_ROUTING, "Content: %s", os_routing_to_string(&rbuf, &rt.p)); - - /* - * sometimes netlink messages from the kernel have a (random?) sequence number, so - * we deliver everything with seq==0 and with an unregistered sequence number - * to the attached listeners now - */ - filter = NULL; - if (msg->nlmsg_seq != 0) { - /* check for feedback for ongoing route commands */ - filter = avl_find_element(&_rtnetlink_feedback, &msg->nlmsg_seq, filter, _internal._node); + if (result > 0) { + OONF_DEBUG(LOG_OS_ROUTING, "Ignore incoming routing message"); + return; } - if (filter) { - if (filter->cb_get != NULL && _match_routes(filter, &rt)) { - filter->cb_get(filter, &rt); - } - } - else { - /* send route events to listeners */ - list_for_each_element(&_rtnetlink_listener, listener, _internal._node) { - listener->cb_get(&rt, msg->nlmsg_type == RTM_NEWROUTE); - } + OONF_DEBUG(LOG_OS_ROUTING, "%s %s", msg->nlmsg_type == RTM_NEWROUTE ? "route added" : "route removed", + os_routing_to_string(&rbuf, &netlink_payload.p)); + + /* send route events to listeners */ + list_for_each_element(&_rtnetlink_listener, listener, _internal._node) { + listener->cb_get(&netlink_payload, msg->nlmsg_type == RTM_NEWROUTE); } } /** - * Handle feedback from netlink socket - * @param seq sequence number of netlink response - * @param err error value + * Handle incoming rtnetlink messages + * @param msg netlink message including header */ static void -_cb_rtnetlink_error(uint32_t seq, int err) { - struct os_route *route; +_cb_rtnetlink_response(struct os_system_netlink_message *msg, struct nlmsghdr *header) { + struct os_route *filter; + struct os_route netlink_payload; + int result; + #ifdef OONF_LOG_DEBUG_INFO struct os_route_str rbuf; #endif - route = avl_find_element(&_rtnetlink_feedback, &seq, route, _internal._node); - if (route) { - OONF_DEBUG(LOG_OS_ROUTING, "Route seqno %u failed: %s (%d) %s", seq, strerror(err), err, - os_routing_to_string(&rbuf, &route->p)); + if (header->nlmsg_type != RTM_NEWROUTE && header->nlmsg_type != RTM_DELROUTE) { + OONF_DEBUG(LOG_OS_ROUTING, "Received (unnecessary) netlink message type %u", header->nlmsg_type); + return; + } - _routing_finished(route, err); + result = _routing_parse_nlmsg(&netlink_payload, header); + if (result < 0) { + OONF_WARN(LOG_OS_ROUTING, "Error while processing route reply (result %d)", result); + return; } - else { - OONF_DEBUG(LOG_OS_ROUTING, "Unknown route with seqno %u failed: %s (%d)", seq, strerror(err), err); + if (result > 0) { + OONF_DEBUG(LOG_OS_ROUTING, "Ignore incoming routing message"); + return; + } + + OONF_DEBUG(LOG_OS_ROUTING, "%s %s", header->nlmsg_type == RTM_NEWROUTE ? "route added" : "route removed", + os_routing_to_string(&rbuf, &netlink_payload.p)); + + filter = container_of(msg, struct os_route, _internal.msg); + if (_match_routes(filter, &netlink_payload)) { + filter->cb_get(filter, &netlink_payload); } } /** - * Handle ack timeout from netlink socket + * Handle feedback from netlink socket + * @param seq sequence number of netlink response + * @param err error value */ static void -_cb_rtnetlink_timeout(void) { - struct os_route *route, *rt_it; +_cb_rtnetlink_error(struct os_system_netlink_message *nl_msg) { + struct os_route *route; +#ifdef OONF_LOG_DEBUG_INFO + struct os_route_str rbuf; +#endif - OONF_WARN(LOG_OS_ROUTING, "Netlink timeout for routing"); + route = container_of(nl_msg, struct os_route, _internal.msg); + OONF_DEBUG(LOG_OS_ROUTING, "Route seqno %u failed: %s (%d) %s", + nl_msg->message->nlmsg_seq, strerror(nl_msg->result), nl_msg->result, os_routing_to_string(&rbuf, &route->p)); - avl_for_each_element_safe(&_rtnetlink_feedback, route, _internal._node, rt_it) { - _routing_finished(route, -1); - } + _routing_finished(route, nl_msg->result); } /** @@ -700,17 +696,16 @@ _cb_rtnetlink_timeout(void) { * @param seq netlink sequence number */ static void -_cb_rtnetlink_done(uint32_t seq) { +_cb_rtnetlink_done(struct os_system_netlink_message *nl_msg) { struct os_route *route; #ifdef OONF_LOG_DEBUG_INFO struct os_route_str rbuf; #endif - OONF_DEBUG(LOG_OS_ROUTING, "Got done: %u", seq); + OONF_DEBUG(LOG_OS_ROUTING, "received done message (seq=%u)", nl_msg->message->nlmsg_seq); - route = avl_find_element(&_rtnetlink_feedback, &seq, route, _internal._node); - if (route) { - OONF_DEBUG(LOG_OS_ROUTING, "Route %s with seqno %u done", os_routing_to_string(&rbuf, &route->p), seq); - _routing_finished(route, 0); - } + route = container_of(nl_msg, struct os_route, _internal.msg); + OONF_DEBUG(LOG_OS_ROUTING, "Route done (seq=%u): %s", + nl_msg->message->nlmsg_seq, os_routing_to_string(&rbuf, &route->p)); + _routing_finished(route, 0); } diff --git a/src/base/os_linux/os_system_linux.c b/src/base/os_linux/os_system_linux.c index 693b2d2f..259d3b5b 100644 --- a/src/base/os_linux/os_system_linux.c +++ b/src/base/os_linux/os_system_linux.c @@ -62,9 +62,13 @@ #include #include +#include +#include +#include #include #include #include +#include #include #include @@ -79,31 +83,58 @@ /* Definitions */ #define LOG_OS_SYSTEM _oonf_os_system_subsystem.logging +enum { + NETLINK_MESSAGE_BLOCK_SIZE = 4096, +}; + /* prototypes */ static int _init(void); static void _cleanup(void); static void _cb_handle_netlink_timeout(struct oonf_timer_instance *); static void _netlink_handler(struct oonf_socket_entry *entry); -static void _enqueue_netlink_buffer(struct os_system_netlink *nl); -static void _handle_nl_err(struct os_system_netlink *, struct nlmsghdr *); -static void _flush_netlink_buffer(struct os_system_netlink *nl); /* static buffers for receiving/sending a netlink message */ -static struct sockaddr_nl _netlink_nladdr = { .nl_family = AF_NETLINK }; +static struct sockaddr_nl _netlink_nladdr = { + .nl_family = AF_NETLINK, + .nl_pid = 0, + .nl_groups = 0, +}; -static struct iovec _netlink_rcv_iov; -static struct msghdr _netlink_rcv_msg = { &_netlink_nladdr, sizeof(_netlink_nladdr), &_netlink_rcv_iov, 1, NULL, 0, 0 }; +static void *_netlink_rcv_buffer = NULL; +static size_t _netlink_rcv_size = 0; -static struct nlmsghdr _netlink_hdr_done = { .nlmsg_len = sizeof(struct nlmsghdr), .nlmsg_type = NLMSG_DONE }; +static struct iovec _netlink_rcv_iov = { + .iov_base = NULL, + .iov_len = 0 +}; -static struct iovec _netlink_send_iov[] = { - { NULL, 0 }, - { &_netlink_hdr_done, sizeof(_netlink_hdr_done) }, +static struct msghdr _netlink_rcv_msg = { + .msg_name = &_netlink_nladdr, + .msg_namelen = sizeof(_netlink_nladdr), + .msg_iov = &_netlink_rcv_iov, + .msg_iovlen = 1, + .msg_control = NULL, + .msg_controllen = 0, + .msg_flags = 0 }; -static struct msghdr _netlink_send_msg = { &_netlink_nladdr, sizeof(_netlink_nladdr), _netlink_send_iov, - ARRAYSIZE(_netlink_send_iov), NULL, 0, 0 }; +static struct nlmsghdr _netlink_hdr_done = { + .nlmsg_len = sizeof(struct nlmsghdr), + .nlmsg_type = NLMSG_DONE, + .nlmsg_flags = 0, +}; +static struct iovec _netlink_send_iov[32]; + +static struct msghdr _netlink_send_msg = { + .msg_name = &_netlink_nladdr, + .msg_namelen = sizeof(_netlink_nladdr), + .msg_iov = _netlink_send_iov, + .msg_iovlen = ARRAYSIZE(_netlink_send_iov), + .msg_control = NULL, + .msg_controllen = 0, + .msg_flags = 0 +}; /* netlink timeout handling */ static struct oonf_timer_class _netlink_timer = { @@ -114,6 +145,7 @@ static struct oonf_timer_class _netlink_timer = { /* subsystem definition */ static const char *_dependencies[] = { OONF_SOCKET_SUBSYSTEM, + OONF_CLASS_SUBSYSTEM, }; static struct oonf_subsystem _oonf_os_system_subsystem = { @@ -131,8 +163,13 @@ static uint32_t _seq_used = 0; /* global ioctl sockets for ipv4 and ipv6 */ static int _ioctl_v4, _ioctl_v6; -/* empty netlink buffer */ -static struct os_system_netlink_buffer _dummy_buffer; +/* tree of netlink protocols */ +static struct avl_tree _netlink_protocol_tree; + +static struct oonf_class _netlink_protocol = { + .name = "netlink protocol", + .size = sizeof(struct os_system_netlink_socket), +}; /** * Initialize os-specific subsystem @@ -140,9 +177,16 @@ static struct os_system_netlink_buffer _dummy_buffer; */ static int _init(void) { + _netlink_rcv_buffer = calloc(NETLINK_MESSAGE_BLOCK_SIZE, 1); + if (!_netlink_rcv_buffer) { + return -1; + } + _netlink_rcv_size = NETLINK_MESSAGE_BLOCK_SIZE; + _ioctl_v4 = socket(AF_INET, SOCK_DGRAM, 0); if (_ioctl_v4 == -1) { OONF_WARN(LOG_OS_SYSTEM, "Cannot open ipv4 ioctl socket: %s (%d)", strerror(errno), errno); + free(_netlink_rcv_buffer); return -1; } @@ -152,6 +196,8 @@ _init(void) { } oonf_timer_add(&_netlink_timer); + avl_init(&_netlink_protocol_tree, avl_comp_int32, false); + oonf_class_add(&_netlink_protocol); return 0; } @@ -160,11 +206,13 @@ _init(void) { */ static void _cleanup(void) { + oonf_class_remove(&_netlink_protocol); oonf_timer_remove(&_netlink_timer); close(_ioctl_v4); if (_ioctl_v6 != -1) { close(_ioctl_v6); } + free(_netlink_rcv_buffer); } /** @@ -242,189 +290,182 @@ os_system_linux_linux_get_ioctl_fd(int af_type) { } } -/** - * Open a new bidirectional netlink socket - * @param nl pointer to initialized netlink socket handler - * @param protocol protocol id (NETLINK_ROUTING for example) - * @return -1 if an error happened, 0 otherwise - */ -int -os_system_linux_netlink_add(struct os_system_netlink *nl, int protocol) { +static struct os_system_netlink_socket * +_add_netlink(int32_t protocol) { + struct os_system_netlink_socket *nl_sock; + static uint32_t socket_id = 0; struct sockaddr_nl addr; int recvbuf; int fd; + nl_sock = avl_find_element(&_netlink_protocol_tree, &protocol, nl_sock, _node); + if (nl_sock) { + return nl_sock; + } + + nl_sock = oonf_class_malloc(&_netlink_protocol); + if (!nl_sock) { + return NULL; + } + fd = socket(PF_NETLINK, SOCK_RAW, protocol); if (fd < 0) { - OONF_WARN(nl->used_by->logging, "Cannot open netlink socket '%s': %s (%d)", nl->name, strerror(errno), errno); + OONF_WARN(LOG_OS_SYSTEM, "Cannot open netlink socket type %d: %s (%d)", + protocol, strerror(errno), errno); goto os_add_netlink_fail; } - if (os_fd_init(&nl->socket.fd, fd)) { - OONF_WARN(nl->used_by->logging, "Could not initialize socket representation"); + if (os_fd_init(&nl_sock->nl_socket.fd, fd)) { + OONF_WARN(LOG_OS_SYSTEM, "Netlink %d: Could not initialize socket representation", protocol); goto os_add_netlink_fail; } - if (abuf_init(&nl->out)) { - OONF_WARN(nl->used_by->logging, - "Not enough memory for" - " netlink '%s'output buffer", - nl->name); - goto os_add_netlink_fail; - } - abuf_memcpy(&nl->out, &_dummy_buffer, sizeof(_dummy_buffer)); - - nl->in = calloc(1, getpagesize()); - if (nl->in == NULL) { - OONF_WARN(nl->used_by->logging, "Not enough memory for netlink '%s' input buffer", nl->name); + nl_sock->in = calloc(1, NETLINK_MESSAGE_BLOCK_SIZE); + if (nl_sock->in == NULL) { + OONF_WARN(LOG_OS_SYSTEM, "Netlink type %d: Not enough memory for input buffer", protocol); goto os_add_netlink_fail; } - nl->in_len = getpagesize(); + nl_sock->in_max_len = NETLINK_MESSAGE_BLOCK_SIZE; memset(&addr, 0, sizeof(addr)); addr.nl_family = AF_NETLINK; + addr.nl_pid = (((uint32_t)getpid()) & ((1u<<22)-1)) + (socket_id << 22); + nl_sock->pid = addr.nl_pid; + socket_id++; #if defined(SO_RCVBUF) - recvbuf = 65536 * 16; - if (setsockopt(nl->socket.fd.fd, SOL_SOCKET, SO_RCVBUF, &recvbuf, sizeof(recvbuf))) { - OONF_WARN(nl->used_by->logging, - "Cannot setup receive buffer size for" - " netlink socket '%s': %s (%d)\n", - nl->name, strerror(errno), errno); + recvbuf = 65536; + if (setsockopt(nl_sock->nl_socket.fd.fd, SOL_SOCKET, SO_RCVBUF, &recvbuf, sizeof(recvbuf))) { + OONF_WARN(LOG_OS_SYSTEM, + "Netlink type %d: Cannot setup receive buffer size for socket: %s (%d)\n", + protocol, strerror(errno), errno); } #endif - if (bind(nl->socket.fd.fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) { - OONF_WARN(nl->used_by->logging, "Could not bind netlink socket %s: %s (%d)", nl->name, strerror(errno), errno); + if (bind(nl_sock->nl_socket.fd.fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) { + OONF_WARN(LOG_OS_SYSTEM, "Netlink type %d: Could not bind socket: %s (%d)", protocol, strerror(errno), errno); goto os_add_netlink_fail; } - nl->socket.name = "os_system_netlink"; - nl->socket.process = _netlink_handler; - oonf_socket_add(&nl->socket); - oonf_socket_set_read(&nl->socket, true); + nl_sock->nl_socket.name = "os_system_netlink"; + nl_sock->nl_socket.process = _netlink_handler; + oonf_socket_add(&nl_sock->nl_socket); + oonf_socket_set_read(&nl_sock->nl_socket, true); - nl->timeout.class = &_netlink_timer; + nl_sock->timeout.class = &_netlink_timer; - list_init_head(&nl->buffered); - return 0; + OONF_DEBUG(LOG_OS_SYSTEM, "Netlink type %d: Bound netlink socket pid %u", + protocol, addr.nl_pid); + + nl_sock->netlink_type = protocol; + nl_sock->_node.key = &nl_sock->netlink_type; + avl_insert(&_netlink_protocol_tree, &nl_sock->_node); + + list_init_head(&nl_sock->buffered_messages); + list_init_head(&nl_sock->sent_messages); + list_init_head(&nl_sock->handlers); + return nl_sock; os_add_netlink_fail: - os_fd_invalidate(&nl->socket.fd); + os_fd_invalidate(&nl_sock->nl_socket.fd); if (fd != -1) { close(fd); } - free(nl->in); - abuf_free(&nl->out); - fd = -1; - return -1; + free(nl_sock->in); + return NULL; } /** - * Close a netlink socket handler - * @param nl pointer to handler + * Open a new bidirectional netlink socket + * @param nl pointer to initialized netlink socket handler + * @param protocol protocol id (NETLINK_ROUTING for example) + * @return -1 if an error happened, 0 otherwise */ -void -os_system_linux_netlink_remove(struct os_system_netlink *nl) { - if (os_fd_is_initialized(&nl->socket.fd)) { - oonf_socket_remove(&nl->socket); +int +os_system_linux_netlink_add(struct os_system_netlink *nl, int protocol) { + size_t i; + nl->nl_socket = _add_netlink(protocol); + if (!nl->nl_socket) { + return -1; + } + + for (i = 0; i < nl->multicast_count; i++) { + if (setsockopt(os_fd_get_fd(&nl->nl_socket->nl_socket.fd), + SOL_NETLINK, NETLINK_ADD_MEMBERSHIP, &nl->multicast[i], sizeof(nl->multicast[i]))) { + OONF_WARN(nl->used_by->logging, "Netlink '%s': could not join mc group: %d", + nl->name, nl->multicast[i]); + return -1; + } + } + + list_add_tail(&nl->nl_socket->handlers, &nl->_node); + return 0; +} - os_fd_close(&nl->socket.fd); - free(nl->in); - abuf_free(&nl->out); +static void +_remove_protocol(struct os_system_netlink_socket *nl_socket) { + if (os_fd_is_initialized(&nl_socket->nl_socket.fd)) { + oonf_socket_remove(&nl_socket->nl_socket); + + os_fd_close(&nl_socket->nl_socket.fd); } + free(nl_socket->in); + avl_remove(&_netlink_protocol_tree, &nl_socket->_node); + oonf_class_free(&_netlink_protocol, nl_socket); } /** - * add netlink message to buffer - * @param nl netlink message + * Close a netlink socket handler + * @param nl pointer to handler */ -static void -_enqueue_netlink_buffer(struct os_system_netlink *nl) { - struct os_system_netlink_buffer *bufptr; - - /* initialize new buffer */ - bufptr = (struct os_system_netlink_buffer *)abuf_getptr(&nl->out); - bufptr->total = abuf_getlen(&nl->out) - sizeof(*bufptr); - bufptr->messages = nl->out_messages; - - /* append to end of queue */ - list_add_tail(&nl->buffered, &bufptr->_node); - nl->out_messages = 0; +void +os_system_linux_netlink_remove(struct os_system_netlink *nl) { + struct os_system_netlink_socket *nl_socket; + nl_socket = nl->nl_socket; - /* get a new outgoing buffer */ - abuf_init(&nl->out); - abuf_memcpy(&nl->out, &_dummy_buffer, sizeof(_dummy_buffer)); + list_remove(&nl->_node); + if (!list_is_empty(&nl_socket->handlers)) { + return; + } + _remove_protocol(nl_socket); } /** * Add a netlink message to the outgoign queue of a handler * @param nl pointer to netlink handler * @param nl_hdr pointer to netlink message - * @return sequence number used for message */ -int -os_system_linux_netlink_send(struct os_system_netlink *nl, struct nlmsghdr *nl_hdr) { +void +os_system_linux_netlink_send(struct os_system_netlink *nl, struct os_system_netlink_message *msg) { + struct os_system_netlink_socket *nl_socket; + struct nlmsghdr *hdr; + + nl_socket = nl->nl_socket; + hdr = msg->message; + OONF_ASSERT(msg->message, LOG_OS_SYSTEM, "no netlink message"); _seq_used = (_seq_used + 1) & INT32_MAX; - OONF_DEBUG( - nl->used_by->logging, "Prepare to send netlink '%s' message %u (%u bytes)", nl->name, _seq_used, nl_hdr->nlmsg_len); - - nl_hdr->nlmsg_seq = _seq_used; - nl_hdr->nlmsg_flags |= NLM_F_ACK | NLM_F_MULTI; - - if (nl_hdr->nlmsg_len + abuf_getlen(&nl->out) > (size_t)getpagesize()) { - _enqueue_netlink_buffer(nl); + if (_seq_used == 0) { + _seq_used++; } - abuf_memcpy(&nl->out, nl_hdr, nl_hdr->nlmsg_len); - OONF_DEBUG_HEX(nl->used_by->logging, nl_hdr, nl_hdr->nlmsg_len, "Content of netlink '%s' message:", nl->name); + /* initialize os_system header */ + msg->dump = (hdr->nlmsg_flags & NLM_F_DUMP) == NLM_F_DUMP; + msg->originator = nl; + msg->result = -1; - nl->out_messages++; + /* finish netlink header */ + hdr->nlmsg_seq = _seq_used; + hdr->nlmsg_pid = nl_socket->pid; + hdr->nlmsg_flags |= NLM_F_ACK; - /* trigger write */ - if (nl->msg_in_transit == 0) { - oonf_socket_set_write(&nl->socket, true); - } - return _seq_used; -} - -/** - * Join a list of multicast groups for a netlink socket - * @param nl pointer to netlink handler - * @param groups pointer to array of multicast groups - * @param groupcount number of entries in groups array - * @return -1 if an error happened, 0 otherwise - */ -int -os_system_linux_netlink_add_mc(struct os_system_netlink *nl, const uint32_t *groups, size_t groupcount) { - size_t i; + OONF_DEBUG_HEX(nl->used_by->logging, hdr, hdr->nlmsg_len, + "Netlink '%s': Append message (type=%u, len=%u, seq=%u, pid=%u, flags=0x%04x)", + nl->name, hdr->nlmsg_type, hdr->nlmsg_len, hdr->nlmsg_seq, hdr->nlmsg_pid, hdr->nlmsg_flags); - for (i = 0; i < groupcount; i++) { - if (setsockopt(os_fd_get_fd(&nl->socket.fd), SOL_NETLINK, NETLINK_ADD_MEMBERSHIP, &groups[i], sizeof(groups[i]))) { - OONF_WARN(nl->used_by->logging, "Could not join netlink '%s' mc group: %x", nl->name, groups[i]); - return -1; - } - } - return 0; -} - -/** - * Leave a list of multicast groups for a netlink socket - * @param nl pointer to netlink handler - * @param groups pointer to array of multicast groups - * @param groupcount number of entries in groups array - * @return -1 if an error happened, 0 otherwise - */ -int -os_system_linux_netlink_drop_mc(struct os_system_netlink *nl, const int *groups, size_t groupcount) { - size_t i; - - for (i = 0; i < groupcount; i++) { - if (setsockopt(os_fd_get_fd(&nl->socket.fd), SOL_NETLINK, NETLINK_DROP_MEMBERSHIP, &groups[i], sizeof(groups[i]))) { - OONF_WARN(nl->used_by->logging, "Could not drop netlink '%s' mc group: %x", nl->name, groups[i]); - return -1; - } + /* trigger write */ + if (list_is_empty(&nl_socket->buffered_messages) && list_is_empty(&nl_socket->sent_messages)) { + oonf_socket_set_write(&nl_socket->nl_socket, true); } - return 0; + list_add_tail(&nl->nl_socket->buffered_messages, &msg->_node); } /** @@ -437,26 +478,28 @@ os_system_linux_netlink_drop_mc(struct os_system_netlink *nl, const int *groups, * @return -1 if netlink message got too large, 0 otherwise */ int -os_system_linux_netlink_addreq( - struct os_system_netlink *nl, struct nlmsghdr *nlmsg, int type, const void *data, int len) { +os_system_linux_netlink_addreq(struct os_system_netlink_message *nl_msg, int type, const void *data, int len) { + struct nlmsghdr *hdr; struct nlattr *nl_attr; size_t aligned_msg_len, aligned_attr_len; + hdr = nl_msg->message; + /* calculate aligned length of message and new attribute */ - aligned_msg_len = NLMSG_ALIGN(nlmsg->nlmsg_len); - aligned_attr_len = NLA_HDRLEN + len; + aligned_msg_len = NLMSG_ALIGN(hdr->nlmsg_len); + aligned_attr_len = NLMSG_ALIGN(NLA_HDRLEN + len); - if (aligned_msg_len + aligned_attr_len > UIO_MAXIOV) { - OONF_WARN(LOG_OS_SYSTEM, "Netlink '%s' message got too large!", nl->name); + if (aligned_msg_len + aligned_attr_len > nl_msg->max_length) { + OONF_WARN(LOG_OS_SYSTEM, "Netlink '%s:' message got too large!", nl_msg->originator->name); return -1; } - nl_attr = (struct nlattr *)((void *)((char *)nlmsg + aligned_msg_len)); + nl_attr = (struct nlattr *)((void *)((char *)hdr + aligned_msg_len)); nl_attr->nla_type = type; nl_attr->nla_len = aligned_attr_len; /* fix length of netlink message */ - nlmsg->nlmsg_len = aligned_msg_len + aligned_attr_len; + hdr->nlmsg_len = aligned_msg_len + aligned_attr_len; if (len) { memcpy((char *)nl_attr + NLA_HDRLEN, data, len); @@ -470,98 +513,126 @@ os_system_linux_netlink_addreq( */ static void _cb_handle_netlink_timeout(struct oonf_timer_instance *ptr) { - struct os_system_netlink *nl; - - nl = container_of(ptr, struct os_system_netlink, timeout); - - if (nl->cb_timeout) { - nl->cb_timeout(); + struct os_system_netlink_socket *nl_socket; + struct os_system_netlink_message *msg, *msg_it; + + nl_socket = container_of(ptr, struct os_system_netlink_socket, timeout); + + list_for_each_element_safe(&nl_socket->sent_messages, msg, _node, msg_it) { + if (msg->originator->cb_error) { + msg->originator->cb_error(msg); + } + list_remove(&msg->_node); } - nl->msg_in_transit = 0; + + oonf_socket_set_write(&nl_socket->nl_socket, !list_is_empty(&nl_socket->buffered_messages)); } -/** - * Send all netlink messages in the outgoing queue to the kernel - * @param nl pointer to netlink handler - */ static void -_flush_netlink_buffer(struct os_system_netlink *nl) { - struct os_system_netlink_buffer *buffer; +_send_netlink_messages(struct os_system_netlink_socket *nl_socket) { + struct os_system_netlink_message *nl_msg, *nl_msg_it; + size_t i, count, size; + struct nlmsghdr *nl_hdr; ssize_t ret; int err; - - if (nl->msg_in_transit > 0) { - oonf_socket_set_write(&nl->socket, false); + if (!list_is_empty(&nl_socket->sent_messages)) { + /* still messages in transit */ return; } - - if (list_is_empty(&nl->buffered)) { - if (abuf_getlen(&nl->out) > sizeof(struct os_system_netlink_buffer)) { - _enqueue_netlink_buffer(nl); - } - else { - oonf_socket_set_write(&nl->socket, false); - return; - } + if (list_is_empty(&nl_socket->buffered_messages)) { + oonf_socket_set_write(&nl_socket->nl_socket, false); + return; } + + count = 0; + size = _netlink_hdr_done.nlmsg_len; + + nl_msg = list_first_element(&nl_socket->buffered_messages, nl_msg, _node); + do { + _netlink_send_iov[count].iov_base = nl_msg->message; + _netlink_send_iov[count].iov_len = nl_msg->message->nlmsg_len; + + OONF_INFO(LOG_OS_SYSTEM, "Sending netlink message from %s with seq %d", + nl_msg->originator->name, nl_msg->message->nlmsg_seq); - /* get first buffer */ - buffer = list_first_element(&nl->buffered, buffer, _node); + /* move to sent list */ + list_remove(&nl_msg->_node); + list_add_tail(&nl_socket->sent_messages, &nl_msg->_node); + count++; + size += nl_msg->message->nlmsg_len; - /* send outgoing message */ - _netlink_send_iov[0].iov_base = (char *)(buffer) + sizeof(*buffer); - _netlink_send_iov[0].iov_len = buffer->total; + if (nl_msg->dump) { + /* no aggregation of dump netlink commands */ + break; + } - if ((ret = sendmsg(os_fd_get_fd(&nl->socket.fd), &_netlink_send_msg, MSG_DONTWAIT)) <= 0) { + nl_msg = list_first_element(&nl_socket->buffered_messages, nl_msg, _node); + } while (!list_is_empty(&nl_socket->buffered_messages) + && count < ARRAYSIZE(_netlink_send_iov)-1 + && !nl_msg->dump + && size + nl_msg->message->nlmsg_len < NETLINK_MESSAGE_BLOCK_SIZE); + + /* fix IO vector */ + if (count > 1) { + for (i=0; inlmsg_flags |= NLM_F_MULTI; + } + _netlink_send_iov[count].iov_base = &_netlink_hdr_done; + _netlink_send_iov[count].iov_len = sizeof(_netlink_hdr_done); + } + _netlink_send_msg.msg_iovlen = count; + + if ((ret = sendmsg(os_fd_get_fd(&nl_socket->nl_socket.fd), &_netlink_send_msg, MSG_DONTWAIT)) <= 0) { + /* a transmission error happened */ err = errno; #if EAGAIN == EWOULDBLOCK if (err != EAGAIN) { #else if (err != EAGAIN && err != EWOULDBLOCK) { #endif - OONF_WARN(nl->used_by->logging, - "Cannot send data (%" PRINTF_SIZE_T_SPECIFIER " bytes)" - " to netlink socket %s: %s (%d)", - abuf_getlen(&nl->out), nl->name, strerror(err), err); - - /* remove netlink message from internal queue */ - nl->cb_error(nl->in->nlmsg_seq, err); + /* something serious happened */ + OONF_WARN(LOG_OS_SYSTEM, + "Netlink '%d': Cannot send data (%" PRINTF_SIZE_T_SPECIFIER " bytes): %s (%d)", + nl_socket->netlink_type, size, strerror(err), err); + + /* report error */ + list_for_each_element_safe(&nl_socket->sent_messages, nl_msg, _node, nl_msg_it) { + list_remove(&nl_msg->_node); + nl_msg->result = err; + if (nl_msg->originator->cb_error) { + nl_msg->originator->cb_error(nl_msg); + } + } + } + else { + /* just try again later, shuffle messages back to transmission queue */ + list_for_each_element_reverse_safe(&nl_socket->sent_messages, nl_msg, _node, nl_msg_it) { + list_remove(&nl_msg->_node); + list_add_head(&nl_socket->buffered_messages, &nl_msg->_node); + } } } else { - nl->msg_in_transit += buffer->messages; - - OONF_DEBUG(nl->used_by->logging, "netlink %s: Sent %u bytes (%u messages in transit)", nl->name, buffer->total, - nl->msg_in_transit); + OONF_DEBUG(LOG_OS_SYSTEM, "Netlink '%d': Sent %"PRINTF_SSIZE_T_SPECIFIER" bytes " + "(%"PRINTF_SIZE_T_SPECIFIER" messages in transit)", + nl_socket->netlink_type, size, count); /* start feedback timer */ - oonf_timer_set(&nl->timeout, OS_SYSTEM_NETLINK_TIMEOUT); + oonf_timer_set(&nl_socket->timeout, OS_SYSTEM_NETLINK_TIMEOUT); } - - list_remove(&buffer->_node); - free(buffer); - - oonf_socket_set_write(&nl->socket, !list_is_empty(&nl->buffered)); } -/** - * Cleanup netlink handler because all outstanding jobs - * are finished - * @param nl pointer to os_system_netlink handler - */ -static void -_netlink_job_finished(struct os_system_netlink *nl) { - if (nl->msg_in_transit > 0) { - nl->msg_in_transit--; - } - if (nl->msg_in_transit == 0) { - oonf_timer_stop(&nl->timeout); +static struct os_system_netlink_message * +_find_matching_message(struct os_system_netlink_socket *nl_socket, uint32_t seqno) { + struct os_system_netlink_message *nl_msg; - if (!list_is_empty(&nl->buffered) || nl->out_messages > 0) { - oonf_socket_set_write(&nl->socket, true); + list_for_each_element(&nl_socket->sent_messages, nl_msg, _node) { + if (nl_msg->message->nlmsg_seq == seqno) { + return nl_msg; } } - OONF_DEBUG(nl->used_by->logging, "netlink '%s' finished: %d still in transit", nl->name, nl->msg_in_transit); + return NULL; } /** @@ -570,17 +641,18 @@ _netlink_job_finished(struct os_system_netlink *nl) { */ static void _netlink_handler(struct oonf_socket_entry *entry) { - struct os_system_netlink *nl; + struct os_system_netlink_socket *nl_socket; + struct os_system_netlink_message *nl_msg; + struct os_system_netlink *nl_handler; struct nlmsghdr *nh; + struct nlmsgerr *err; ssize_t ret; - size_t len; + size_t len, i; int flags; - uint32_t current_seq = 0; - bool trigger_is_done; - nl = container_of(entry, typeof(*nl), socket); + nl_socket = container_of(entry, typeof(*nl_socket), nl_socket); if (oonf_socket_is_write(entry)) { - _flush_netlink_buffer(nl); + _send_netlink_messages(nl_socket); } if (!oonf_socket_is_read(entry)) { @@ -592,78 +664,60 @@ _netlink_handler(struct oonf_socket_entry *entry) { flags = MSG_PEEK; netlink_rcv_retry: - _netlink_rcv_iov.iov_base = nl->in; - _netlink_rcv_iov.iov_len = nl->in_len; + _netlink_rcv_iov.iov_base = _netlink_rcv_buffer; + _netlink_rcv_iov.iov_len = _netlink_rcv_size; - OONF_DEBUG(nl->used_by->logging, - "Read netlink '%s' message with" - " %" PRINTF_SIZE_T_SPECIFIER " bytes buffer", - nl->name, nl->in_len); if ((ret = recvmsg(entry->fd.fd, &_netlink_rcv_msg, MSG_DONTWAIT | flags)) < 0) { #if EAGAIN == EWOULDBLOCK if (errno != EAGAIN) { #else if (errno != EAGAIN && errno != EWOULDBLOCK) { #endif - OONF_WARN(nl->used_by->logging, "netlink '%s' recvmsg error: %s (%d)\n", nl->name, strerror(errno), errno); + OONF_WARN(LOG_OS_SYSTEM, "Netlink '%d' recvmsg error: %s (%d)\n", nl_socket->netlink_type, strerror(errno), errno); } else { - oonf_socket_set_read(&nl->socket, true); + oonf_socket_set_read(&nl_socket->nl_socket, true); } return; } /* not enough buffer space ? */ - if (nl->in_len < (size_t)ret || (_netlink_rcv_msg.msg_flags & MSG_TRUNC) != 0) { + if (_netlink_rcv_size < (size_t)ret || (_netlink_rcv_msg.msg_flags & MSG_TRUNC) != 0) { void *ptr; - ret = ret / getpagesize(); + ret = ret / NETLINK_MESSAGE_BLOCK_SIZE; ret++; - ret *= getpagesize(); + ret *= NETLINK_MESSAGE_BLOCK_SIZE; - ptr = realloc(nl->in, ret); + ptr = realloc(_netlink_rcv_buffer, ret); if (!ptr) { - OONF_WARN(nl->used_by->logging, - "Not enough memory to" - " increase netlink '%s' input buffer", - nl->name); + OONF_WARN(LOG_OS_SYSTEM, + "Netlink '%d': Not enough memory to increase input buffer to %" PRINTF_SSIZE_T_SPECIFIER, + nl_socket->netlink_type, ret); return; } - nl->in = ptr; - nl->in_len = ret; + OONF_INFO(LOG_OS_SYSTEM, + "Netlink '%d': increased input buffer to %" PRINTF_SSIZE_T_SPECIFIER, + nl_socket->netlink_type, ret); + _netlink_rcv_buffer = ptr; + _netlink_rcv_size = ret; goto netlink_rcv_retry; } if (flags) { /* it worked, not remove the message from the queue */ flags = 0; - OONF_DEBUG(nl->used_by->logging, - "Got estimate of netlink '%s'" - " message size, retrieve it", - nl->name); goto netlink_rcv_retry; } - OONF_DEBUG(nl->used_by->logging, "Got netlink '%s' message of %" PRINTF_SSIZE_T_SPECIFIER " bytes", nl->name, ret); - OONF_DEBUG_HEX(nl->used_by->logging, nl->in, ret, "Content of netlink '%s' message:", nl->name); - - trigger_is_done = false; + OONF_DEBUG_HEX(LOG_OS_SYSTEM, _netlink_rcv_buffer, ret, + "Netlink '%d': recv data(bytes=%" PRINTF_SSIZE_T_SPECIFIER ")", + nl_socket->netlink_type, ret); /* loop through netlink headers */ len = (size_t)ret; - for (nh = nl->in; NLMSG_OK(nh, len); nh = NLMSG_NEXT(nh, len)) { - OONF_DEBUG( - nl->used_by->logging, "Netlink '%s' message received: type %d seq %u\n", nl->name, nh->nlmsg_type, nh->nlmsg_seq); - - if (nh == nl->in) { - current_seq = nh->nlmsg_seq; - } - - if (current_seq != nh->nlmsg_seq && trigger_is_done) { - if (nl->cb_done) { - nl->cb_done(current_seq); - } - trigger_is_done = false; - } + for (nh = _netlink_rcv_buffer; NLMSG_OK(nh, len); nh = NLMSG_NEXT(nh, len)) { + OONF_DEBUG(LOG_OS_SYSTEM, "Netlink '%d': recv msg(type=%u, len=%u, seq=%u, pid=%u, flags=0x%04x)\n", + nl_socket->netlink_type, nh->nlmsg_type, nh->nlmsg_len, nh->nlmsg_seq, nh->nlmsg_pid, nh->nlmsg_flags); switch (nh->nlmsg_type) { case NLMSG_NOOP: @@ -671,61 +725,61 @@ _netlink_handler(struct oonf_socket_entry *entry) { case NLMSG_DONE: /* End of a multipart netlink message reached */ - trigger_is_done = true; + nl_msg = _find_matching_message(nl_socket, nh->nlmsg_seq); + if (nl_msg && nl_msg->dump) { + list_remove(&nl_msg->_node); + nl_msg->originator->cb_done(nl_msg); + } break; case NLMSG_ERROR: /* Feedback for async netlink message */ - trigger_is_done = false; - _handle_nl_err(nl, nh); + err = (struct nlmsgerr *)NLMSG_DATA(nh); + nl_msg = _find_matching_message(nl_socket, err->msg.nlmsg_seq); + if (nl_msg) { + list_remove(&nl_msg->_node); + + if (err->error < 0) { + nl_msg->result = -err->error; + } + else { + nl_msg->result = err->error; + } + + if (err->error == 0) { + nl_msg->originator->cb_done(nl_msg); + } + else { + nl_msg->originator->cb_error(nl_msg); + } + } break; default: - if (nl->cb_message) { - nl->cb_message(nh); + nl_msg = _find_matching_message(nl_socket, nh->nlmsg_seq); + if (nl_msg != NULL && nl_msg->originator->nl_socket->pid == nh->nlmsg_pid && nl_msg->dump) { + /* this is a response to a netlink dump */ + nl_msg->originator->cb_response(nl_msg, nh); + } + else { + /* this seems to be multicast */ + list_for_each_element(&nl_socket->handlers, nl_handler, _node) { + for (i=0; imulticast_count; i++) { + if (nl_handler->multicast[i] == nh->nlmsg_type) { + nl_handler->cb_multicast(nl_handler, nh); + break; + } + } + } } break; } } - if (trigger_is_done) { - oonf_timer_stop(&nl->timeout); - if (nl->cb_done) { - nl->cb_done(current_seq); - } - _netlink_job_finished(nl); - } - /* reset timeout if necessary */ - if (oonf_timer_is_active(&nl->timeout)) { - oonf_timer_set(&nl->timeout, OS_SYSTEM_NETLINK_TIMEOUT); - } -} - -/** - * Handle result code in netlink message - * @param nl pointer to netlink handler - * @param nh pointer to netlink message - */ -static void -_handle_nl_err(struct os_system_netlink *nl, struct nlmsghdr *nh) { - struct nlmsgerr *err; - - err = (struct nlmsgerr *)NLMSG_DATA(nh); - - OONF_DEBUG(nl->used_by->logging, "Received netlink '%s' seq %u feedback (%u bytes): %s (%d)", nl->name, nh->nlmsg_seq, - nh->nlmsg_len, strerror(-err->error), -err->error); - - if (err->error) { - if (nl->cb_error) { - nl->cb_error(err->msg.nlmsg_seq, -err->error); - } + if (list_is_empty(&nl_socket->sent_messages)) { + oonf_timer_stop(&nl_socket->timeout); } - else { - if (nl->cb_done) { - nl->cb_done(err->msg.nlmsg_seq); - } - } - - _netlink_job_finished(nl); + oonf_socket_set_write(&nl_socket->nl_socket, + list_is_empty(&nl_socket->sent_messages) && !list_is_empty(&nl_socket->buffered_messages)); } diff --git a/src/generic/nl80211_listener/nl80211_get_interface.c b/src/generic/nl80211_listener/nl80211_get_interface.c index ef0df7ee..f0b6f8e0 100644 --- a/src/generic/nl80211_listener/nl80211_get_interface.c +++ b/src/generic/nl80211_listener/nl80211_get_interface.c @@ -89,20 +89,19 @@ static uint64_t _get_bandwidth(uint32_t width); /** * Send a netlink message to get the nl80211 interface information - * @param nl pointer to netlink handler * @param nl_msg pointer to netlink message * @param hdr pointer to generic netlink header * @param interf nl80211 listener interface */ void nl80211_send_get_interface( - struct os_system_netlink *nl, struct nlmsghdr *nl_msg, struct genlmsghdr *hdr, struct nl80211_if *interf) { + struct os_system_netlink_message *nl_msg, struct genlmsghdr *hdr, struct nl80211_if *interf) { int if_index = nl80211_get_if_baseindex(interf); hdr->cmd = NL80211_CMD_GET_INTERFACE; /* add interface index to the request */ - os_system_linux_netlink_addreq(nl, nl_msg, NL80211_ATTR_IFINDEX, &if_index, sizeof(if_index)); + os_system_linux_netlink_addreq(nl_msg, NL80211_ATTR_IFINDEX, &if_index, sizeof(if_index)); } /** diff --git a/src/generic/nl80211_listener/nl80211_get_mpp.c b/src/generic/nl80211_listener/nl80211_get_mpp.c index f828d67d..32469c5a 100644 --- a/src/generic/nl80211_listener/nl80211_get_mpp.c +++ b/src/generic/nl80211_listener/nl80211_get_mpp.c @@ -95,14 +95,14 @@ */ void nl80211_send_get_mpp( - struct os_system_netlink *nl, struct nlmsghdr *nl_msg, struct genlmsghdr *hdr, struct nl80211_if *interf) { + struct os_system_netlink_message *nl_msg, struct genlmsghdr *hdr, struct nl80211_if *interf) { int if_index = nl80211_get_if_baseindex(interf); hdr->cmd = NL80211_CMD_GET_MPP; - nl_msg->nlmsg_flags |= NLM_F_DUMP; + nl_msg->message->nlmsg_flags |= NLM_F_DUMP; /* add interface index to the request */ - os_system_linux_netlink_addreq(nl, nl_msg, NL80211_ATTR_IFINDEX, &if_index, sizeof(if_index)); + os_system_linux_netlink_addreq(nl_msg, NL80211_ATTR_IFINDEX, &if_index, sizeof(if_index)); } /** diff --git a/src/generic/nl80211_listener/nl80211_get_station_dump.c b/src/generic/nl80211_listener/nl80211_get_station_dump.c index fd758712..d06a80c1 100644 --- a/src/generic/nl80211_listener/nl80211_get_station_dump.c +++ b/src/generic/nl80211_listener/nl80211_get_station_dump.c @@ -99,14 +99,14 @@ static int64_t _get_bitrate(struct nlattr *bitrate_attr); */ void nl80211_send_get_station_dump( - struct os_system_netlink *nl, struct nlmsghdr *nl_msg, struct genlmsghdr *hdr, struct nl80211_if *interf) { + struct os_system_netlink_message *nl_msg, struct genlmsghdr *hdr, struct nl80211_if *interf) { int if_index = nl80211_get_if_baseindex(interf); hdr->cmd = NL80211_CMD_GET_STATION; - nl_msg->nlmsg_flags |= NLM_F_DUMP; + nl_msg->message->nlmsg_flags |= NLM_F_DUMP; /* add interface index to the request */ - os_system_linux_netlink_addreq(nl, nl_msg, NL80211_ATTR_IFINDEX, &if_index, sizeof(if_index)); + os_system_linux_netlink_addreq(nl_msg, NL80211_ATTR_IFINDEX, &if_index, sizeof(if_index)); } /** diff --git a/src/generic/nl80211_listener/nl80211_get_survey.c b/src/generic/nl80211_listener/nl80211_get_survey.c index 58eb3c44..2a28b476 100644 --- a/src/generic/nl80211_listener/nl80211_get_survey.c +++ b/src/generic/nl80211_listener/nl80211_get_survey.c @@ -94,14 +94,14 @@ */ void nl80211_send_get_survey( - struct os_system_netlink *nl, struct nlmsghdr *nl_msg, struct genlmsghdr *hdr, struct nl80211_if *interf) { + struct os_system_netlink_message *nl_msg, struct genlmsghdr *hdr, struct nl80211_if *interf) { int if_index = nl80211_get_if_baseindex(interf); hdr->cmd = NL80211_CMD_GET_SURVEY; - nl_msg->nlmsg_flags |= NLM_F_DUMP; + nl_msg->message->nlmsg_flags |= NLM_F_DUMP; /* add interface index to the request */ - os_system_linux_netlink_addreq(nl, nl_msg, NL80211_ATTR_IFINDEX, &if_index, sizeof(if_index)); + os_system_linux_netlink_addreq(nl_msg, NL80211_ATTR_IFINDEX, &if_index, sizeof(if_index)); } /** diff --git a/src/generic/nl80211_listener/nl80211_get_wiphy.c b/src/generic/nl80211_listener/nl80211_get_wiphy.c index c8e16f5d..447f32d8 100644 --- a/src/generic/nl80211_listener/nl80211_get_wiphy.c +++ b/src/generic/nl80211_listener/nl80211_get_wiphy.c @@ -278,19 +278,19 @@ static struct rate_info _mcs_table_80211n_40[77] = { */ void nl80211_send_get_wiphy( - struct os_system_netlink *nl, struct nlmsghdr *nl_msg, struct genlmsghdr *hdr, struct nl80211_if *interf) { + struct os_system_netlink_message *nl_msg, struct genlmsghdr *hdr, struct nl80211_if *interf) { /* clear maximum data rates */ interf->max_rx = 0; interf->max_tx = 0; hdr->cmd = NL80211_CMD_GET_WIPHY; - nl_msg->nlmsg_flags |= NLM_F_DUMP; + nl_msg->message->nlmsg_flags |= NLM_F_DUMP; /* add "split wiphy dump" flag */ - os_system_linux_netlink_addreq(nl, nl_msg, NL80211_ATTR_SPLIT_WIPHY_DUMP, NULL, 0); + os_system_linux_netlink_addreq(nl_msg, NL80211_ATTR_SPLIT_WIPHY_DUMP, NULL, 0); /* add interface index to the request */ - os_system_linux_netlink_addreq(nl, nl_msg, NL80211_ATTR_WIPHY, &interf->wifi_phy_if, sizeof(interf->wifi_phy_if)); + os_system_linux_netlink_addreq(nl_msg, NL80211_ATTR_WIPHY, &interf->wifi_phy_if, sizeof(interf->wifi_phy_if)); OONF_DEBUG(LOG_NL80211, "Send GET_WIPHY to phydev %d", interf->wifi_phy_if); } diff --git a/src/generic/nl80211_listener/nl80211_listener.c b/src/generic/nl80211_listener/nl80211_listener.c index eb1995c6..c6f4cb25 100644 --- a/src/generic/nl80211_listener/nl80211_listener.c +++ b/src/generic/nl80211_listener/nl80211_listener.c @@ -105,13 +105,12 @@ struct _nl80211_query { /** * Callback to send query - * @param nl netlink handler - * @param nl_msg netlink message header + * @param nl_msg netlink message * @param hdr generic message header * @param interf netlink interface */ void (*send)( - struct os_system_netlink *nl, struct nlmsghdr *nl_msg, struct genlmsghdr *hdr, struct nl80211_if *interf); + struct os_system_netlink_message *nl_msg, struct genlmsghdr *hdr, struct nl80211_if *interf); /** * Callback to process incoming netlink data @@ -170,10 +169,8 @@ static void _cb_if_config_changed(void); static void _cb_transmission_event(struct oonf_timer_instance *); static void _trigger_next_netlink_query(void); -static void _cb_nl_message(struct nlmsghdr *hdr); -static void _cb_nl_error(uint32_t seq, int error); -static void _cb_nl_timeout(void); -static void _cb_nl_done(uint32_t seq); +static void _cb_nl_response(struct os_system_netlink_message* nl_msg, struct nlmsghdr* hdr); +static void _cb_nl_feedback(struct os_system_netlink_message *nl); /* configuration */ static struct cfg_schema_section _if_section = { @@ -233,15 +230,19 @@ enum oonf_log_source LOG_NL80211; static struct os_system_netlink _netlink_handler = { .name = "nl80211 listener", .used_by = &_nl80211_listener_subsystem, - .cb_message = _cb_nl_message, - .cb_error = _cb_nl_error, - .cb_done = _cb_nl_done, - .cb_timeout = _cb_nl_timeout, + .cb_response = _cb_nl_response, + .cb_error = _cb_nl_feedback, + .cb_done = _cb_nl_feedback, }; /* buffer for outgoing netlink message */ -static uint32_t _nl_msgbuffer[UIO_MAXIOV / 4]; +static uint32_t _nl_msgbuffer[UIO_MAXIOV / sizeof(uint32_t)]; static struct nlmsghdr *_nl_msg = (void *)_nl_msgbuffer; +static struct os_system_netlink_message os_nl_msg = { + .message = (void *)_nl_msgbuffer, + .max_length = sizeof(_nl_msgbuffer), + .originator = &_netlink_handler, +}; /* netlink nl80211 identification */ static uint32_t _nl80211_id = 0; @@ -588,10 +589,10 @@ _send_netlink_message(struct nl80211_if *interf, enum _if_query query) { genl_send_get_family(_nl_msg, hdr); } else if (_if_query_ops[query].send) { - _if_query_ops[query].send(&_netlink_handler, _nl_msg, hdr, interf); + _if_query_ops[query].send(&os_nl_msg, hdr, interf); } - os_system_linux_netlink_send(&_netlink_handler, _nl_msg); + os_system_linux_netlink_send(&_netlink_handler, &os_nl_msg); } /** @@ -676,7 +677,7 @@ _trigger_next_netlink_query(void) { * @param hdr pointer to netlink message */ static void -_cb_nl_message(struct nlmsghdr *hdr) { +_cb_nl_response(struct os_system_netlink_message *nl_msg __attribute__((unused)), struct nlmsghdr *hdr) { struct genlmsghdr *gen_hdr; gen_hdr = NLMSG_DATA(hdr); @@ -706,34 +707,9 @@ _cb_nl_message(struct nlmsghdr *hdr) { * @param error error code */ static void -_cb_nl_error(uint32_t seq __attribute((unused)), int error __attribute((unused))) { - OONF_INFO(LOG_NL80211, "seq %u: Received error %d", seq, error); - if (_nl80211_id && _nl80211_multicast_group) { - _trigger_next_netlink_query(); - } -} - -/** - * Callback triggered when one or more netlink messages time out - */ -static void -_cb_nl_timeout(void) { - OONF_INFO(LOG_NL80211, "Received timeout"); - if (_nl80211_id && _nl80211_multicast_group) { - if (_if_query_ops[_current_query_number].finalize) { - _if_query_ops[_current_query_number].finalize(_current_query_if); - } - _trigger_next_netlink_query(); - } -} - -/** - * Callback triggered when a netlink message is done - * @param seq sequence number - */ -static void -_cb_nl_done(uint32_t seq __attribute((unused))) { - OONF_INFO(LOG_NL80211, "%u: Received done", seq); +_cb_nl_feedback(struct os_system_netlink_message *nlmsg __attribute__((unused))) { + OONF_INFO(LOG_NL80211, "seq %u: Result %d", + nlmsg->message->nlmsg_seq, nlmsg->result); if (_nl80211_id && _nl80211_multicast_group) { if (_if_query_ops[_current_query_number].finalize) { _if_query_ops[_current_query_number].finalize(_current_query_if); From 00b20a5a8629a8e1413027a4573dcd9afc6fad6d Mon Sep 17 00:00:00 2001 From: Henning Rogge Date: Wed, 16 Jan 2019 10:46:05 +0100 Subject: [PATCH 07/63] Fix some comment/formatting issues --- src/base/os_linux/os_interface_linux.c | 12 +- src/base/os_linux/os_routing_linux.c | 25 ++- src/base/os_linux/os_system_linux.c | 228 ++++++++++++++----------- 3 files changed, 147 insertions(+), 118 deletions(-) diff --git a/src/base/os_linux/os_interface_linux.c b/src/base/os_linux/os_interface_linux.c index 267bdb4a..48e24ce3 100644 --- a/src/base/os_linux/os_interface_linux.c +++ b/src/base/os_linux/os_interface_linux.c @@ -218,7 +218,7 @@ static struct avl_tree _interface_data_tree; static const char _ANY_INTERFACE[] = OS_INTERFACE_ANY; /** - * Initialize os-specific subsystem + * Initialize os-specific interface subsystem * @return -1 if an error happened, 0 otherwise */ static int @@ -271,7 +271,7 @@ _init(void) { } /** - * Cleanup os-specific subsystem + * Cleanup os-specific interface subsystem */ static void _cleanup(void) { @@ -1142,6 +1142,11 @@ _address_parse_nlmsg(const char *ifname, struct nlmsghdr *msg) { } } +/** +* Process a response to a netlink query +* @param nl_msg original netlink query +* @param hdr response of the kernel +*/ static void _cb_rtnetlink_response(struct os_system_netlink_message *nl_msg, struct nlmsghdr *hdr) { _cb_rtnetlink_multicast(nl_msg->originator, hdr); @@ -1183,8 +1188,7 @@ _cb_rtnetlink_multicast(struct os_system_netlink *nl __attribute__((unused)), st /** * Handle feedback from netlink socket - * @param seq sequence number of netlink message - * @param error error code + * @param nl_msg netlink message we got feedback/timeout for */ static void _cb_rtnetlink_feedback(struct os_system_netlink_message *nl_msg) { diff --git a/src/base/os_linux/os_routing_linux.c b/src/base/os_linux/os_routing_linux.c index 286a5b18..0a5ddaa4 100644 --- a/src/base/os_linux/os_routing_linux.c +++ b/src/base/os_linux/os_routing_linux.c @@ -111,7 +111,7 @@ static struct route_type_translation _type_translation[] = { { OS_ROUTE_UNICAST, /* netlink socket for route set/get commands */ static const uint32_t _rtnetlink_mcast[] = { RTNLGRP_IPV4_ROUTE, RTNLGRP_IPV6_ROUTE }; -static struct os_system_netlink _rtnetlink_socket = { +static struct os_system_netlink _rtnetlink_handler = { .name = "routing send", .used_by = &_oonf_os_routing_subsystem, @@ -150,7 +150,7 @@ static bool _is_kernel_3_11_0_or_better; */ static int _init(void) { - if (os_system_linux_netlink_add(&_rtnetlink_socket, NETLINK_ROUTE)) { + if (os_system_linux_netlink_add(&_rtnetlink_handler, NETLINK_ROUTE)) { return -1; } @@ -165,9 +165,7 @@ _init(void) { */ static void _cleanup(void) { - /* TODO: remove out netlink handler */ - - os_system_linux_netlink_remove(&_rtnetlink_socket); + os_system_linux_netlink_remove(&_rtnetlink_handler); } /** @@ -257,7 +255,7 @@ os_routing_linux_set(struct os_route *route, bool set, bool del_similar) { } /* cannot fail */ - os_system_linux_netlink_send(&_rtnetlink_socket, &route->_internal.msg); + os_system_linux_netlink_send(&_rtnetlink_handler, &route->_internal.msg); return 0; } @@ -295,7 +293,7 @@ os_routing_linux_query(struct os_route *route) { msg->nlmsg_type = RTM_GETROUTE; rt_gen->rtgen_family = route->p.family; - os_system_linux_netlink_send(&_rtnetlink_socket, &route->_internal.msg); + os_system_linux_netlink_send(&_rtnetlink_handler, &route->_internal.msg); return 0; } @@ -597,7 +595,7 @@ _match_routes(struct os_route *filter, struct os_route *route) { } /** - * Handle incoming rtnetlink messages + * Handle incoming rtnetlink multicast messages * @param msg netlink message including header */ static void @@ -635,7 +633,7 @@ _cb_rtnetlink_multicast(struct os_system_netlink *nl __attribute__((unused)), st } /** - * Handle incoming rtnetlink messages + * Handle incoming response for rtnetlink query * @param msg netlink message including header */ static void @@ -673,9 +671,8 @@ _cb_rtnetlink_response(struct os_system_netlink_message *msg, struct nlmsghdr *h } /** - * Handle feedback from netlink socket - * @param seq sequence number of netlink response - * @param err error value + * Handle negative feedback from netlink socket + * @param nl_masg netlink message the feedback is for */ static void _cb_rtnetlink_error(struct os_system_netlink_message *nl_msg) { @@ -692,8 +689,8 @@ _cb_rtnetlink_error(struct os_system_netlink_message *nl_msg) { } /** - * Handle done from multipart netlink messages - * @param seq netlink sequence number + * Handle positive feedback from netlink socket + * @param nl_masg netlink message the feedback is for */ static void _cb_rtnetlink_done(struct os_system_netlink_message *nl_msg) { diff --git a/src/base/os_linux/os_system_linux.c b/src/base/os_linux/os_system_linux.c index 259d3b5b..203fb757 100644 --- a/src/base/os_linux/os_system_linux.c +++ b/src/base/os_linux/os_system_linux.c @@ -91,6 +91,9 @@ enum { static int _init(void); static void _cleanup(void); +static struct os_system_netlink_socket *_add_protocol(int32_t protocol); +static void _remove_protocol(struct os_system_netlink_socket *nl_socket); + static void _cb_handle_netlink_timeout(struct oonf_timer_instance *); static void _netlink_handler(struct oonf_socket_entry *entry); @@ -166,7 +169,7 @@ static int _ioctl_v4, _ioctl_v6; /* tree of netlink protocols */ static struct avl_tree _netlink_protocol_tree; -static struct oonf_class _netlink_protocol = { +static struct oonf_class _netlink_protocol_class = { .name = "netlink protocol", .size = sizeof(struct os_system_netlink_socket), }; @@ -197,7 +200,7 @@ _init(void) { oonf_timer_add(&_netlink_timer); avl_init(&_netlink_protocol_tree, avl_comp_int32, false); - oonf_class_add(&_netlink_protocol); + oonf_class_add(&_netlink_protocol_class); return 0; } @@ -206,7 +209,12 @@ _init(void) { */ static void _cleanup(void) { - oonf_class_remove(&_netlink_protocol); + struct os_system_netlink_socket *nlp, *nlp_it; + + avl_for_each_element_safe(&_netlink_protocol_tree, nlp, _node, nlp_it) { + _remove_protocol(nlp); + } + oonf_class_remove(&_netlink_protocol_class); oonf_timer_remove(&_netlink_timer); close(_ioctl_v4); if (_ioctl_v6 != -1) { @@ -290,90 +298,6 @@ os_system_linux_linux_get_ioctl_fd(int af_type) { } } -static struct os_system_netlink_socket * -_add_netlink(int32_t protocol) { - struct os_system_netlink_socket *nl_sock; - static uint32_t socket_id = 0; - struct sockaddr_nl addr; - int recvbuf; - int fd; - - nl_sock = avl_find_element(&_netlink_protocol_tree, &protocol, nl_sock, _node); - if (nl_sock) { - return nl_sock; - } - - nl_sock = oonf_class_malloc(&_netlink_protocol); - if (!nl_sock) { - return NULL; - } - - fd = socket(PF_NETLINK, SOCK_RAW, protocol); - if (fd < 0) { - OONF_WARN(LOG_OS_SYSTEM, "Cannot open netlink socket type %d: %s (%d)", - protocol, strerror(errno), errno); - goto os_add_netlink_fail; - } - - if (os_fd_init(&nl_sock->nl_socket.fd, fd)) { - OONF_WARN(LOG_OS_SYSTEM, "Netlink %d: Could not initialize socket representation", protocol); - goto os_add_netlink_fail; - } - nl_sock->in = calloc(1, NETLINK_MESSAGE_BLOCK_SIZE); - if (nl_sock->in == NULL) { - OONF_WARN(LOG_OS_SYSTEM, "Netlink type %d: Not enough memory for input buffer", protocol); - goto os_add_netlink_fail; - } - nl_sock->in_max_len = NETLINK_MESSAGE_BLOCK_SIZE; - - memset(&addr, 0, sizeof(addr)); - addr.nl_family = AF_NETLINK; - addr.nl_pid = (((uint32_t)getpid()) & ((1u<<22)-1)) + (socket_id << 22); - nl_sock->pid = addr.nl_pid; - socket_id++; - -#if defined(SO_RCVBUF) - recvbuf = 65536; - if (setsockopt(nl_sock->nl_socket.fd.fd, SOL_SOCKET, SO_RCVBUF, &recvbuf, sizeof(recvbuf))) { - OONF_WARN(LOG_OS_SYSTEM, - "Netlink type %d: Cannot setup receive buffer size for socket: %s (%d)\n", - protocol, strerror(errno), errno); - } -#endif - - if (bind(nl_sock->nl_socket.fd.fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) { - OONF_WARN(LOG_OS_SYSTEM, "Netlink type %d: Could not bind socket: %s (%d)", protocol, strerror(errno), errno); - goto os_add_netlink_fail; - } - - nl_sock->nl_socket.name = "os_system_netlink"; - nl_sock->nl_socket.process = _netlink_handler; - oonf_socket_add(&nl_sock->nl_socket); - oonf_socket_set_read(&nl_sock->nl_socket, true); - - nl_sock->timeout.class = &_netlink_timer; - - OONF_DEBUG(LOG_OS_SYSTEM, "Netlink type %d: Bound netlink socket pid %u", - protocol, addr.nl_pid); - - nl_sock->netlink_type = protocol; - nl_sock->_node.key = &nl_sock->netlink_type; - avl_insert(&_netlink_protocol_tree, &nl_sock->_node); - - list_init_head(&nl_sock->buffered_messages); - list_init_head(&nl_sock->sent_messages); - list_init_head(&nl_sock->handlers); - return nl_sock; - -os_add_netlink_fail: - os_fd_invalidate(&nl_sock->nl_socket.fd); - if (fd != -1) { - close(fd); - } - free(nl_sock->in); - return NULL; -} - /** * Open a new bidirectional netlink socket * @param nl pointer to initialized netlink socket handler @@ -383,7 +307,7 @@ _add_netlink(int32_t protocol) { int os_system_linux_netlink_add(struct os_system_netlink *nl, int protocol) { size_t i; - nl->nl_socket = _add_netlink(protocol); + nl->nl_socket = _add_protocol(protocol); if (!nl->nl_socket) { return -1; } @@ -401,18 +325,6 @@ os_system_linux_netlink_add(struct os_system_netlink *nl, int protocol) { return 0; } -static void -_remove_protocol(struct os_system_netlink_socket *nl_socket) { - if (os_fd_is_initialized(&nl_socket->nl_socket.fd)) { - oonf_socket_remove(&nl_socket->nl_socket); - - os_fd_close(&nl_socket->nl_socket.fd); - } - free(nl_socket->in); - avl_remove(&_netlink_protocol_tree, &nl_socket->_node); - oonf_class_free(&_netlink_protocol, nl_socket); -} - /** * Close a netlink socket handler * @param nl pointer to handler @@ -507,6 +419,111 @@ os_system_linux_netlink_addreq(struct os_system_netlink_message *nl_msg, int typ return 0; } +/** +* Add new protocol instance of netlink socket +* @param protocol protocol id +* @return pointer to new netlink protocol, NULL if failed to allocate +*/ +static struct os_system_netlink_socket * +_add_protocol(int32_t protocol) { + struct os_system_netlink_socket *nl_sock; + static uint32_t socket_id = 0; + struct sockaddr_nl addr; + int recvbuf; + int fd; + + nl_sock = avl_find_element(&_netlink_protocol_tree, &protocol, nl_sock, _node); + if (nl_sock) { + return nl_sock; + } + + nl_sock = oonf_class_malloc(&_netlink_protocol_class); + if (!nl_sock) { + return NULL; + } + + fd = socket(PF_NETLINK, SOCK_RAW, protocol); + if (fd < 0) { + OONF_WARN(LOG_OS_SYSTEM, "Cannot open netlink socket type %d: %s (%d)", + protocol, strerror(errno), errno); + goto os_add_netlink_fail; + } + + if (os_fd_init(&nl_sock->nl_socket.fd, fd)) { + OONF_WARN(LOG_OS_SYSTEM, "Netlink %d: Could not initialize socket representation", protocol); + goto os_add_netlink_fail; + } + nl_sock->in = calloc(1, NETLINK_MESSAGE_BLOCK_SIZE); + if (nl_sock->in == NULL) { + OONF_WARN(LOG_OS_SYSTEM, "Netlink type %d: Not enough memory for input buffer", protocol); + goto os_add_netlink_fail; + } + nl_sock->in_max_len = NETLINK_MESSAGE_BLOCK_SIZE; + + memset(&addr, 0, sizeof(addr)); + addr.nl_family = AF_NETLINK; + addr.nl_pid = (((uint32_t)getpid()) & ((1u<<22)-1)) + (socket_id << 22); + nl_sock->pid = addr.nl_pid; + socket_id++; + +#if defined(SO_RCVBUF) + recvbuf = 65536; + if (setsockopt(nl_sock->nl_socket.fd.fd, SOL_SOCKET, SO_RCVBUF, &recvbuf, sizeof(recvbuf))) { + OONF_WARN(LOG_OS_SYSTEM, + "Netlink type %d: Cannot setup receive buffer size for socket: %s (%d)\n", + protocol, strerror(errno), errno); + } +#endif + + if (bind(nl_sock->nl_socket.fd.fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) { + OONF_WARN(LOG_OS_SYSTEM, "Netlink type %d: Could not bind socket: %s (%d)", protocol, strerror(errno), errno); + goto os_add_netlink_fail; + } + + nl_sock->nl_socket.name = "os_system_netlink"; + nl_sock->nl_socket.process = _netlink_handler; + oonf_socket_add(&nl_sock->nl_socket); + oonf_socket_set_read(&nl_sock->nl_socket, true); + + nl_sock->timeout.class = &_netlink_timer; + + OONF_DEBUG(LOG_OS_SYSTEM, "Netlink type %d: Bound netlink socket pid %u", + protocol, addr.nl_pid); + + nl_sock->netlink_type = protocol; + nl_sock->_node.key = &nl_sock->netlink_type; + avl_insert(&_netlink_protocol_tree, &nl_sock->_node); + + list_init_head(&nl_sock->buffered_messages); + list_init_head(&nl_sock->sent_messages); + list_init_head(&nl_sock->handlers); + return nl_sock; + +os_add_netlink_fail: + os_fd_invalidate(&nl_sock->nl_socket.fd); + if (fd != -1) { + close(fd); + } + free(nl_sock->in); + return NULL; +} + +/** +* Remove netlink protocol instance +* @param nl_socket netlink protocol instance +*/ +static void +_remove_protocol(struct os_system_netlink_socket *nl_socket) { + if (os_fd_is_initialized(&nl_socket->nl_socket.fd)) { + oonf_socket_remove(&nl_socket->nl_socket); + + os_fd_close(&nl_socket->nl_socket.fd); + } + free(nl_socket->in); + avl_remove(&_netlink_protocol_tree, &nl_socket->_node); + oonf_class_free(&_netlink_protocol_class, nl_socket); +} + /** * Handle timeout of netlink acks * @param ptr timer instance that fired @@ -529,6 +546,11 @@ _cb_handle_netlink_timeout(struct oonf_timer_instance *ptr) { } static void +/** +* Collects a block of non-dumping (or a single dumping query) and sends them out +* to the kernel netlink subsystem +* @param nl_socket netlink protocol instance +*/ _send_netlink_messages(struct os_system_netlink_socket *nl_socket) { struct os_system_netlink_message *nl_msg, *nl_msg_it; size_t i, count, size; @@ -623,6 +645,12 @@ _send_netlink_messages(struct os_system_netlink_socket *nl_socket) { } } +/** +* Find a netlink message in transfer with a specific sequence number +* @param nl_socket netlink protocol instance +* @param seqno netlink sequence number +* @return netlink message, NULL if not found +*/ static struct os_system_netlink_message * _find_matching_message(struct os_system_netlink_socket *nl_socket, uint32_t seqno) { struct os_system_netlink_message *nl_msg; From 0339b09d8d3df3858a025cf5d479d8f1dc6e0ef4 Mon Sep 17 00:00:00 2001 From: Henning Rogge Date: Tue, 5 Feb 2019 15:57:50 +0100 Subject: [PATCH 08/63] keep track of peer addresses in L2 db when cleaning up --- src/base/oonf_layer2.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/base/oonf_layer2.c b/src/base/oonf_layer2.c index 66a642cf..88561314 100644 --- a/src/base/oonf_layer2.c +++ b/src/base/oonf_layer2.c @@ -545,10 +545,17 @@ oonf_layer2_net_add(const char *ifname) { */ bool oonf_layer2_net_cleanup(struct oonf_layer2_net *l2net, const struct oonf_layer2_origin *origin, bool cleanup_neigh) { + struct oonf_layer2_peer_address *l2peer, *l2peer_it; struct oonf_layer2_neigh *l2neigh; bool changed = false; int i; + avl_for_each_element_safe(&l2net->local_peer_ips, l2peer, _net_node, l2peer_it) { + if (!oonf_layer2_net_remove_ip(l2peer, origin)) { + changed = true; + } + } + for (i = 0; i < OONF_LAYER2_NET_COUNT; i++) { if (l2net->data[i]._origin == origin) { oonf_layer2_data_reset(&l2net->data[i]); @@ -612,6 +619,11 @@ bool oonf_layer2_net_commit(struct oonf_layer2_net *l2net) { size_t i; + if (l2net->local_peer_ips.count > 0) { + oonf_class_event(&_l2network_class, l2net, OONF_OBJECT_CHANGED); + return false; + } + if (l2net->neighbors.count > 0) { oonf_class_event(&_l2network_class, l2net, OONF_OBJECT_CHANGED); return false; From 491ee87d9ded6403aa3cf3efc28f8a3b65872459 Mon Sep 17 00:00:00 2001 From: Henning Rogge Date: Wed, 13 Mar 2019 12:39:43 +0100 Subject: [PATCH 09/63] Fix type in netjsoninfo route output to match the netjson.org schema --- src/olsrv2/netjsoninfo/netjsoninfo.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/olsrv2/netjsoninfo/netjsoninfo.c b/src/olsrv2/netjsoninfo/netjsoninfo.c index 9855d273..c10732a1 100644 --- a/src/olsrv2/netjsoninfo/netjsoninfo.c +++ b/src/olsrv2/netjsoninfo/netjsoninfo.c @@ -792,7 +792,7 @@ _print_routing_tree(struct json_session *session, struct nhdp_domain *domain, in _print_json_netaddr(session, "router_addr", originator); json_end_object(session); - json_start_array(session, JSON_NAME_ROUTE); + json_start_array(session, "routes"); avl_for_each_element(olsrv2_routing_get_tree(domain), rtentry, _node) { if (rtentry->route.p.family == af_type) { From 2f6d196921a2109f3349708ee10d86901daea4c5 Mon Sep 17 00:00:00 2001 From: Henning Rogge Date: Tue, 26 Mar 2019 09:43:19 +0100 Subject: [PATCH 10/63] Add better debugging output for timer values and l2 originators --- include/oonf/base/oonf_clock.h | 2 +- include/oonf/base/oonf_timer.h | 18 ++++++ src/base/oonf_stream_socket.c | 4 +- src/generic/layer2info/layer2info.c | 69 ++++++++++++++++++++++- src/nhdp/nhdpinfo/nhdpinfo.c | 19 ++----- src/olsrv2/netjsoninfo/netjsoninfo.c | 82 +++++++++++++++++++++++++++- src/olsrv2/olsrv2info/olsrv2info.c | 4 +- 7 files changed, 179 insertions(+), 19 deletions(-) diff --git a/include/oonf/base/oonf_clock.h b/include/oonf/base/oonf_clock.h index 3550a1b5..66d04d03 100644 --- a/include/oonf/base/oonf_clock.h +++ b/include/oonf/base/oonf_clock.h @@ -177,7 +177,7 @@ EXPORT uint64_t oonf_clock_getNow(void); EXPORT const char *oonf_clock_toClockString(struct isonumber_str *, uint64_t); /** - * Converts an internal time value into a string representation with + * Converts an internal time interval into a string representation with * the numbers of seconds (including milliseconds as fractions) * @param buf target buffer * @param i time value diff --git a/include/oonf/base/oonf_timer.h b/include/oonf/base/oonf_timer.h index 38640add..3bf2c139 100644 --- a/include/oonf/base/oonf_timer.h +++ b/include/oonf/base/oonf_timer.h @@ -163,6 +163,24 @@ oonf_timer_get_due(const struct oonf_timer_instance *timer) { return oonf_clock_get_relative(timer->_clock); } +/** + * Puts the number of seconds until the timer fires into the output buffer, + * or "-1" if it has already fired. + * @param buf target buffer + * @param timer timer + * @return pointer to string representation + */ +static INLINE const char * +oonf_timer_to_string(struct isonumber_str *buf, struct oonf_timer_instance *timer) { + static const char NONE[] = "-1"; + if (!oonf_timer_is_active(timer)) { + return NONE; + } + else { + return oonf_clock_toIntervalString(buf, oonf_timer_get_due(timer)); + } +} + /** * This is the one stop shop for all sort of timer manipulation. * Depending on the passed in parameters a new timer is started, diff --git a/src/base/oonf_stream_socket.c b/src/base/oonf_stream_socket.c index ca08fe4e..b5a1001e 100644 --- a/src/base/oonf_stream_socket.c +++ b/src/base/oonf_stream_socket.c @@ -312,7 +312,9 @@ oonf_stream_connect_to(struct oonf_stream_socket *stream_socket, const union net */ void oonf_stream_set_timeout(struct oonf_stream_session *con, uint64_t timeout) { - oonf_timer_set(&con->timeout, timeout); + if (oonf_clock_get_absolute(timeout) > oonf_timer_get_due(&con->timeout) + timeout/5) { + oonf_timer_set(&con->timeout, timeout); + } } /** diff --git a/src/generic/layer2info/layer2info.c b/src/generic/layer2info/layer2info.c index 3e078be1..ff31d224 100644 --- a/src/generic/layer2info/layer2info.c +++ b/src/generic/layer2info/layer2info.c @@ -80,6 +80,7 @@ static void _initialize_neigh_data_values(struct oonf_viewer_template *template, static void _initialize_neigh_origin_values(struct oonf_layer2_data *data); static void _initialize_neigh_values(struct oonf_layer2_neigh *neigh); static void _initialize_neigh_ip_values(struct oonf_layer2_neighbor_address *neigh_addr); +static void _initialize_origin_values(struct oonf_layer2_origin *l2origin); static int _cb_create_text_interface(struct oonf_viewer_template *); static int _cb_create_text_interface_ip(struct oonf_viewer_template *); @@ -87,6 +88,7 @@ static int _cb_create_text_neighbor(struct oonf_viewer_template *); static int _cb_create_text_neighbor_ip(struct oonf_viewer_template *); static int _cb_create_text_default(struct oonf_viewer_template *); static int _cb_create_text_dst(struct oonf_viewer_template *); +static int _cb_create_text_origin(struct oonf_viewer_template *); /* * list of template keys and corresponding buffers for values. @@ -166,6 +168,21 @@ static int _cb_create_text_dst(struct oonf_viewer_template *); /*! string suffix for all data originators */ #define KEY_ORIGIN_SUFFIX "_origin" +/*! template key for originator name */ +#define KEY_ORIGIN_NAME "origin_name" + +/*! template key for origin is proactive */ +#define KEY_ORIGIN_PROACTIVE "origin_proactive" + +/*! template key for origin priority */ +#define KEY_ORIGIN_PRIORITY "origin_priority" + +/*! template key for origin produces LIDs */ +#define KEY_ORIGIN_LID "origin_lid" + +/*! template key for origin LID index */ +#define KEY_ORIGIN_LID_INDEX "origin_lid_index" + /* * buffer space for values that will be assembled * into the output of the plugin @@ -193,9 +210,13 @@ static struct netaddr_str _value_neigh_remote_ip_nexthop; static char _value_neigh_remote_ip_origin[IF_NAMESIZE]; static char _value_neigh_data[OONF_LAYER2_NEIGH_COUNT][64]; static char _value_neigh_origin[OONF_LAYER2_NEIGH_COUNT][IF_NAMESIZE]; - static struct netaddr_str _value_dst_addr; static char _value_dst_origin[IF_NAMESIZE]; +static char _value_origin_name[IF_NAMESIZE]; +static char _value_origin_proactive[TEMPLATE_JSON_BOOL_LENGTH]; +static char _value_origin_priority[10]; +static char _value_origin_lid[TEMPLATE_JSON_BOOL_LENGTH]; +static char _value_origin_lid_index[10]; /* definition of the template data entries for JSON and table output */ static struct abuf_template_data_entry _tde_if_key[] = { @@ -248,6 +269,14 @@ static struct abuf_template_data_entry _tde_dst[] = { { KEY_DST_ORIGIN, _value_dst_origin, true }, }; +static struct abuf_template_data_entry _tde_origin[] = { + { KEY_ORIGIN_NAME, _value_origin_name, true }, + { KEY_ORIGIN_PROACTIVE, _value_origin_proactive, true }, + { KEY_ORIGIN_PRIORITY, _value_origin_priority, false }, + { KEY_ORIGIN_LID, _value_origin_lid, true }, + { KEY_ORIGIN_LID_INDEX, _value_origin_lid_index, false }, +}; + static struct abuf_template_storage _template_storage; static struct autobuf _key_storage; @@ -285,6 +314,9 @@ static struct abuf_template_data _td_dst[] = { { _tde_dst_key, ARRAYSIZE(_tde_dst_key) }, { _tde_dst, ARRAYSIZE(_tde_dst) }, }; +static struct abuf_template_data _td_origin[] = { + { _tde_origin, ARRAYSIZE(_tde_origin) }, +}; /* OONF viewer templates (based on Template Data arrays) */ static struct oonf_viewer_template _templates[] = { @@ -324,6 +356,12 @@ static struct oonf_viewer_template _templates[] = { .json_name = "destination", .cb_function = _cb_create_text_dst, }, + { + .data = _td_origin, + .data_size = ARRAYSIZE(_td_origin), + .json_name = "origin", + .cb_function = _cb_create_text_origin, + }, }; /* telnet command of this plugin */ @@ -579,6 +617,19 @@ _initialize_destination_values(struct oonf_layer2_destination *l2dst) { strscpy(_value_dst_origin, l2dst->origin->name, IF_NAMESIZE); } +/** + * Initialize the value buffers for a layer2 originator + * @param l2origin layer2 originator + */ +static void +_initialize_origin_values(struct oonf_layer2_origin *l2origin) { + strscpy(_value_origin_name, l2origin->name, sizeof(_value_origin_name)); + strscpy(_value_origin_proactive, json_getbool(l2origin->proactive), sizeof(_value_origin_proactive)); + snprintf(_value_origin_priority, sizeof(_value_origin_priority), "%d", l2origin->priority); + strscpy(_value_origin_lid, json_getbool(l2origin->lid), sizeof(_value_origin_lid)); + snprintf(_value_origin_lid_index, sizeof(_value_origin_lid_index), "%u", l2origin->lid_index); +} + /** * Callback to generate text/json description of all layer2 interfaces * @param template viewer template @@ -723,3 +774,19 @@ _cb_create_text_dst(struct oonf_viewer_template *template) { } return 0; } + +/** + * Callback to generate text/json description of all layer2 destinations + * @param template viewer template + * @return -1 if an error happened, 0 otherwise + */ +static int +_cb_create_text_origin(struct oonf_viewer_template *template) { + struct oonf_layer2_origin *l2origin; + + avl_for_each_element(oonf_layer2_get_origin_tree(), l2origin, _node) { + _initialize_origin_values(l2origin); + oonf_viewer_output_print_line(template); + } + return 0; +} diff --git a/src/nhdp/nhdpinfo/nhdpinfo.c b/src/nhdp/nhdpinfo/nhdpinfo.c index b2aff36a..f57cec20 100644 --- a/src/nhdp/nhdpinfo/nhdpinfo.c +++ b/src/nhdp/nhdpinfo/nhdpinfo.c @@ -564,14 +564,7 @@ _initialize_interface_address_values(struct nhdp_interface_addr *if_addr) { netaddr_to_string(&_value_if_address, &if_addr->if_addr); strscpy(_value_if_address_lost, json_getbool(if_addr->removed), sizeof(_value_if_address_lost)); - - if (oonf_timer_is_active(&if_addr->_vtime)) { - uint64_t due = oonf_timer_get_due(&if_addr->_vtime); - oonf_clock_toIntervalString(&_value_if_address_vtime, due); - } - else { - strscpy(_value_if_address_vtime.buf, "-1", sizeof(_value_if_address_vtime)); - } + oonf_timer_to_string(&_value_if_address_vtime, &if_addr->_vtime); } /** @@ -585,9 +578,9 @@ _initialize_nhdp_link_values(struct nhdp_link *lnk) { oonf_clock_toIntervalString(&_value_link_vtime_value, lnk->vtime_value); oonf_clock_toIntervalString(&_value_link_itime_value, lnk->itime_value); - oonf_clock_toIntervalString(&_value_link_symtime, oonf_timer_get_due(&lnk->sym_time)); - oonf_clock_toIntervalString(&_value_link_heardtime, oonf_timer_get_due(&lnk->heard_time)); - oonf_clock_toIntervalString(&_value_link_vtime, oonf_timer_get_due(&lnk->vtime)); + oonf_timer_to_string(&_value_link_symtime, &lnk->sym_time); + oonf_timer_to_string(&_value_link_heardtime, &lnk->heard_time); + oonf_timer_to_string(&_value_link_vtime, &lnk->vtime); strscpy(_value_link_status, nhdp_db_link_status_to_string(lnk), sizeof(_value_link_status)); @@ -663,7 +656,7 @@ _initialize_nhdp_link_twohop_values(struct nhdp_l2hop *twohop) { strscpy(_value_twohop_sameif, json_getbool(twohop->same_interface), sizeof(_value_twohop_sameif)); - oonf_clock_toIntervalString(&_value_twohop_vtime, oonf_timer_get_due(&twohop->_vtime)); + oonf_timer_to_string(&_value_twohop_vtime, &twohop->_vtime); } /** @@ -695,7 +688,7 @@ _initialize_nhdp_neighbor_address_values(struct nhdp_naddr *naddr) { strscpy(_value_neighbor_address_lost, json_getbool(oonf_timer_is_active(&naddr->_lost_vtime)), sizeof(_value_neighbor_address_lost)); - oonf_clock_toIntervalString(&_value_neighbor_address_lost_vtime, oonf_timer_get_due(&naddr->_lost_vtime)); + oonf_timer_to_string(&_value_neighbor_address_lost_vtime, &naddr->_lost_vtime); } /** diff --git a/src/olsrv2/netjsoninfo/netjsoninfo.c b/src/olsrv2/netjsoninfo/netjsoninfo.c index c10732a1..f1419157 100644 --- a/src/olsrv2/netjsoninfo/netjsoninfo.c +++ b/src/olsrv2/netjsoninfo/netjsoninfo.c @@ -79,6 +79,9 @@ /*! name of domain command/json-object */ #define JSON_NAME_DOMAIN "domain" +/*! name of domain command/json-object */ +#define JSON_NAME_ID "id" + /*! Text buffer for a domain id string */ struct domain_id_str { /*! string buffer */ @@ -132,6 +135,7 @@ static void _create_graph_json(struct json_session *session, const char *filter) static void _print_routing_tree(struct json_session *session, struct nhdp_domain *domain, int af_type); static void _create_route_json(struct json_session *session, const char *filter); static void _create_domain_json(struct json_session *session); +static void _create_id_json(struct json_session *session); static void _create_error_json(struct json_session *session, const char *message, const char *parameter); static enum oonf_telnet_result _cb_netjsoninfo(struct oonf_telnet_data *con); static void _print_json_string(struct json_session *session, const char *key, const char *value); @@ -149,7 +153,9 @@ static struct oonf_telnet_command _telnet_commands[] = { "The filter prefix use an id (which can be queried by 'domain') to output" " a single domain of route/graph without the NetworkCollection object" " around it. The domain_id's are ipv4_ and ipv6_.\n" - "> netjsoninfo filter route ipv4_0\n"), + "> netjsoninfo filter route ipv4_0\n" + "Netjsoninfo 'id' returns a list of router/prefix id's to the corresponding\n" + "IPs/prefixes\n"), }; /* plugin declaration */ @@ -910,6 +916,77 @@ _create_domain_json(struct json_session *session) { json_end_object(session); } +static void +_create_id_json(struct json_session *session) { + struct olsrv2_tc_node *tc_node; + struct olsrv2_tc_attachment *tc_attached; + struct olsrv2_lan_entry *lan; + struct nhdp_domain *domain; + struct _node_id_str node_id_str; + int af; + + json_start_object(session, NULL); + + _print_json_string(session, "type", "NetworkId"); + _print_json_string(session, "protocol", "olsrv2"); + _print_json_string(session, "version", oonf_log_get_libdata()->version); + _print_json_string(session, "revision", oonf_log_get_libdata()->git_commit); + + json_start_array(session, "routers"); + + /* local router */ + if (olsrv2_originator_get(AF_INET)) { + json_start_object(session, NULL); + _print_json_string(session, "router_id", _get_node_id_me(&node_id_str, AF_INET)); + _print_json_netaddr(session, "router_addr", olsrv2_originator_get(AF_INET)); + json_end_object(session); + } + if (olsrv2_originator_get(AF_INET6)) { + json_start_object(session, NULL); + _print_json_string(session, "router_id", _get_node_id_me(&node_id_str, AF_INET6)); + _print_json_netaddr(session, "router_addr", olsrv2_originator_get(AF_INET6)); + json_end_object(session); + } + + /* remote routers */ + avl_for_each_element(olsrv2_tc_get_tree(), tc_node, _originator_node) { + json_start_object(session, NULL); + _print_json_string(session, "router_id", _get_tc_node_id(&node_id_str, tc_node)); + _print_json_netaddr(session, "router_addr", &tc_node->target.prefix.dst); + json_end_object(session); + } + json_end_array(session); + + json_start_array(session, "attached"); + /* locally attached */ + avl_for_each_element(olsrv2_lan_get_tree(), lan, _node) { + af = netaddr_get_address_family(&lan->prefix.dst); + list_for_each_element(nhdp_domain_get_list(), domain, _node) { + json_start_object(session, NULL); + _print_json_string(session, "attached_id", _get_tc_lan_id(&node_id_str, lan)); + _print_json_string(session, "router_id", _get_node_id_me(&node_id_str, af)); + _print_json_netaddr(session, "router_addr", olsrv2_originator_get(af)); + _print_json_netaddr(session, "attached_dst", &lan->prefix.dst); + _print_json_netaddr(session, "attached_src", &lan->prefix.src); + json_end_object(session); + } + } + /* remote prefixes */ + avl_for_each_element(olsrv2_tc_get_tree(), tc_node, _originator_node) { + avl_for_each_element(&tc_node->_attached_networks, tc_attached, _src_node) { + json_start_object(session, NULL); + _print_json_string(session, "attached_id", _get_tc_endpoint_id(&node_id_str, tc_attached)); + _print_json_string(session, "router_id", _get_tc_node_id(&node_id_str, tc_node)); + _print_json_netaddr(session, "router_addr", &tc_node->target.prefix.dst); + _print_json_netaddr(session, "attached_dst", &tc_attached->dst->target.prefix.dst); + _print_json_netaddr(session, "attached_src", &tc_attached->dst->target.prefix.src); + json_end_object(session); + } + } + json_end_array(session); + json_end_object(session); +} + /** * Print a JSON error * @param session json session @@ -940,6 +1017,9 @@ _handle_netjson_object(struct json_session *session, const char *parameter, bool else if (!filter && (ptr = str_hasnextword(parameter, JSON_NAME_DOMAIN))) { _create_domain_json(session); } + else if (!filter && (ptr = str_hasnextword(parameter, JSON_NAME_ID))) { + _create_id_json(session); + } else { ptr = str_skipnextword(parameter); *error = true; diff --git a/src/olsrv2/olsrv2info/olsrv2info.c b/src/olsrv2/olsrv2info/olsrv2info.c index 50801ca9..ad41817c 100644 --- a/src/olsrv2/olsrv2info/olsrv2info.c +++ b/src/olsrv2/olsrv2info/olsrv2info.c @@ -469,7 +469,7 @@ static void _initialize_old_originator_values(struct olsrv2_originator_set_entry *entry) { netaddr_to_string(&_value_old_originator, &entry->originator); - oonf_clock_toIntervalString(&_value_old_originator_vtime, oonf_timer_get_due(&entry->_vtime)); + oonf_timer_to_string(&_value_old_originator_vtime, &entry->_vtime); } /** @@ -542,7 +542,7 @@ static void _initialize_node_values(struct olsrv2_tc_node *node) { netaddr_to_string(&_value_node, &node->target.prefix.dst); - oonf_clock_toIntervalString(&_value_node_vtime, oonf_timer_get_due(&node->_validity_time)); + oonf_timer_to_string(&_value_node_vtime, &node->_validity_time); snprintf(_value_node_ansn, sizeof(_value_node_ansn), "%u", node->ansn); From 2f0b86e9eeec26febe274f9a9bedcfe6178c9beb Mon Sep 17 00:00:00 2001 From: Henning Rogge Date: Mon, 15 Apr 2019 08:52:36 +0200 Subject: [PATCH 11/63] Add support for Netjson LINK command --- .../nl80211_listener/nl80211_listener.c | 5 +- src/libcommon/json.c | 4 +- src/libcommon/string.c | 2 +- src/nhdp/nhdp/nhdp_domain.c | 9 +- src/nhdp/nhdp/nhdp_writer.c | 2 + src/olsrv2/netjsoninfo/netjsoninfo.c | 351 +++++++++++++++--- 6 files changed, 313 insertions(+), 60 deletions(-) diff --git a/src/generic/nl80211_listener/nl80211_listener.c b/src/generic/nl80211_listener/nl80211_listener.c index c6f4cb25..0846d18f 100644 --- a/src/generic/nl80211_listener/nl80211_listener.c +++ b/src/generic/nl80211_listener/nl80211_listener.c @@ -484,10 +484,6 @@ _nl80211_if_add(const char *name) { return NULL; } - if (interf->l2net->if_type == OONF_LAYER2_TYPE_UNDEFINED) { - interf->l2net->if_type = OONF_LAYER2_TYPE_WIRELESS; - } - /* initialize interface listener */ interf->if_listener.name = interf->name; if (!os_interface_add(&interf->if_listener)) { @@ -697,6 +693,7 @@ _cb_nl_response(struct os_system_netlink_message *nl_msg __attribute__((unused)) } else if (_if_query_ops[_current_query_number].process) { OONF_DEBUG(LOG_NL80211, "Received Nl80211 command %u for query %u", gen_hdr->cmd, _current_query_number); + _current_query_if->l2net->if_type = OONF_LAYER2_TYPE_WIRELESS; _if_query_ops[_current_query_number].process(_current_query_if, hdr); } } diff --git a/src/libcommon/json.c b/src/libcommon/json.c index 14cf0b69..1b863e59 100644 --- a/src/libcommon/json.c +++ b/src/libcommon/json.c @@ -128,7 +128,9 @@ json_print(struct json_session *session, const char *key, bool string, const cha } session->empty = false; - abuf_appendf(session->out, "\"%s\":", key); + if (key) { + abuf_appendf(session->out, "\"%s\":", key); + } _json_printvalue(session->out, value, string); } diff --git a/src/libcommon/string.c b/src/libcommon/string.c index 478e103f..1e3dad29 100644 --- a/src/libcommon/string.c +++ b/src/libcommon/string.c @@ -307,7 +307,7 @@ str_is_printable(const char *value) { * @param buffer_len length of target buffer * @param src binary source * @param src_len length of binary source - * @return -1 if an error happened, length of string otherwise + * @return -1 if an error happened, length of string (without zero byte) otherwise */ ssize_t strhex_from_bin(char *buffer, size_t buffer_len, const uint8_t *src, size_t src_len) { diff --git a/src/nhdp/nhdp/nhdp_domain.c b/src/nhdp/nhdp/nhdp_domain.c index 7e06d5d1..4984d7b4 100644 --- a/src/nhdp/nhdp/nhdp_domain.c +++ b/src/nhdp/nhdp/nhdp_domain.c @@ -441,14 +441,19 @@ void nhdp_domain_process_metric_linktlv(struct nhdp_domain *domain, struct nhdp_link *lnk, const uint8_t *value) { struct rfc7181_metric_field metric_field; uint32_t metric; + bool link, neigh; memcpy(&metric_field, value, sizeof(metric_field)); metric = rfc7181_metric_decode(&metric_field); - if (rfc7181_metric_has_flag(&metric_field, RFC7181_LINKMETRIC_INCOMING_LINK)) { + link = rfc7181_metric_has_flag(&metric_field, RFC7181_LINKMETRIC_INCOMING_LINK); + neigh = rfc7181_metric_has_flag(&metric_field, RFC7181_LINKMETRIC_INCOMING_NEIGH); + OONF_DEBUG(LOG_NHDP_R, "Set incoming %s/%s metric: %u", + link ? "link":"-", neigh ? "neigh":"-", metric); + if (link) { nhdp_domain_get_linkdata(domain, lnk)->metric.out = metric; } - if (rfc7181_metric_has_flag(&metric_field, RFC7181_LINKMETRIC_INCOMING_NEIGH)) { + if (neigh) { nhdp_domain_get_neighbordata(domain, lnk->neigh)->metric.out = metric; } } diff --git a/src/nhdp/nhdp/nhdp_writer.c b/src/nhdp/nhdp/nhdp_writer.c index a72bf4ed..0c702a8b 100644 --- a/src/nhdp/nhdp/nhdp_writer.c +++ b/src/nhdp/nhdp/nhdp_writer.c @@ -399,6 +399,8 @@ _add_link_address(struct rfc5444_writer *writer, struct rfc5444_writer_content_p struct nhdp_link *lnk = NULL; struct nhdp_neighbor *neigh = NULL; + OONF_DEBUG(LOG_NHDP_W, "Determine metric status: %d %d %d", + linkstatus, otherneigh_sym, naddr->neigh->symmetric); if (linkstatus == NHDP_LINK_HEARD || linkstatus == NHDP_LINK_SYMMETRIC) { lnk = laddr->link; } diff --git a/src/olsrv2/netjsoninfo/netjsoninfo.c b/src/olsrv2/netjsoninfo/netjsoninfo.c index f1419157..0d3c400f 100644 --- a/src/olsrv2/netjsoninfo/netjsoninfo.c +++ b/src/olsrv2/netjsoninfo/netjsoninfo.c @@ -82,6 +82,9 @@ /*! name of domain command/json-object */ #define JSON_NAME_ID "id" +/*! name of domain command/json-object */ +#define JSON_NAME_LINK "link" + /*! Text buffer for a domain id string */ struct domain_id_str { /*! string buffer */ @@ -139,7 +142,9 @@ static void _create_id_json(struct json_session *session); static void _create_error_json(struct json_session *session, const char *message, const char *parameter); static enum oonf_telnet_result _cb_netjsoninfo(struct oonf_telnet_data *con); static void _print_json_string(struct json_session *session, const char *key, const char *value); -static void _print_json_number(struct json_session *session, const char *key, uint64_t value); +static void _print_json_bool(struct json_session *session, const char *key, bool value); +static void _print_json_integer(struct json_session *session, const char *key, uint64_t value); +static void _print_json_number(struct json_session *session, const char *key, const char *value); static void _print_json_netaddr(struct json_session *session, const char *key, const struct netaddr *addr); /* telnet command of this plugin */ @@ -481,11 +486,11 @@ _print_edge_links( _print_json_netaddr(session, "target_addr", &lnk->if_addr); cost = nhdp_domain_get_linkdata(domain, lnk)->metric.out; - _print_json_number(session, "cost", cost); + _print_json_integer(session, "cost", cost); _print_json_string(session, "cost_text", nhdp_domain_get_link_metric_value(&mbuf, domain, cost)); cost = nhdp_domain_get_linkdata(domain, lnk)->metric.in; - _print_json_number(session, "in_cost", cost); + _print_json_integer(session, "in_cost", cost); _print_json_string(session, "in_text", nhdp_domain_get_link_metric_value(&mbuf, domain, cost)); _print_json_string(session, "outgoing_tree", json_getbool(outgoing && best_link == lnk)); @@ -526,12 +531,12 @@ _print_graph_edge(struct json_session *session, struct nhdp_domain *domain, cons _print_json_string(session, "source", src->buf); _print_json_string(session, "target", dst->buf); - _print_json_number(session, "cost", out); + _print_json_integer(session, "cost", out); _print_json_string(session, "cost_text", nhdp_domain_get_link_metric_value(&mbuf, domain, out)); json_start_object(session, "properties"); if (in >= RFC7181_METRIC_MIN && in <= RFC7181_METRIC_MAX) { - _print_json_number(session, "in_cost", in); + _print_json_integer(session, "in_cost", in); _print_json_string(session, "in_text", nhdp_domain_get_link_metric_value(&mbuf, domain, in)); } _print_json_string(session, "outgoing_tree", json_getbool(outgoing_tree)); @@ -543,7 +548,7 @@ _print_graph_edge(struct json_session *session, struct nhdp_domain *domain, cons _print_json_netaddr(session, "target_addr", dst_addr); } if (hopcount) { - _print_json_number(session, "hopcount", hopcount); + _print_json_integer(session, "hopcount", hopcount); } switch (type) { @@ -814,7 +819,7 @@ _print_routing_tree(struct json_session *session, struct nhdp_domain *domain, in _print_json_netaddr(session, "next", &rtentry->route.p.gw); _print_json_string(session, "device", if_indextoname(rtentry->route.p.if_index, ibuf)); - _print_json_number(session, "cost", rtentry->path_cost); + _print_json_integer(session, "cost", rtentry->path_cost); _print_json_string( session, "cost_text", nhdp_domain_get_path_metric_value(&mbuf, domain, rtentry->path_cost, rtentry->path_hops)); @@ -826,7 +831,7 @@ _print_routing_tree(struct json_session *session, struct nhdp_domain *domain, in _print_json_string(session, "next_router_id", idbuf.buf); _print_json_netaddr(session, "next_router_addr", &rtentry->next_originator); - _print_json_number(session, "hops", rtentry->path_hops); + _print_json_integer(session, "hops", rtentry->path_hops); _get_node_id(&idbuf, &rtentry->last_originator, NULL); _print_json_string(session, "last_router_id", idbuf.buf); @@ -885,7 +890,7 @@ _create_domain_json(struct json_session *session) { json_start_object(session, NULL); _print_json_string(session, "id", _create_domain_id(&dbuf, domain, AF_INET)); - _print_json_number(session, "number", domain->ext); + _print_json_integer(session, "number", domain->ext); _get_node_id_me(&idbuf, AF_INET); _print_json_string(session, "router_id", idbuf.buf); @@ -900,7 +905,7 @@ _create_domain_json(struct json_session *session) { json_start_object(session, NULL); _print_json_string(session, "id", _create_domain_id(&dbuf, domain, AF_INET6)); - _print_json_number(session, "number", domain->ext); + _print_json_integer(session, "number", domain->ext); _get_node_id_me(&idbuf, AF_INET6); _print_json_string(session, "router_id", idbuf.buf); @@ -916,14 +921,34 @@ _create_domain_json(struct json_session *session) { json_end_object(session); } +static void +_create_local_attached_ids(struct json_session *session, + const struct netaddr *originator) { + struct olsrv2_lan_entry *lan; + struct _node_id_str node_id_str; + int af; + + af = netaddr_get_address_family(originator); + avl_for_each_element(olsrv2_lan_get_tree(), lan, _node) { + if (netaddr_get_address_family(&lan->prefix.dst) == af) { + json_start_object(session, NULL); + _print_json_string(session, "id", _get_tc_lan_id(&node_id_str, lan)); + json_start_object(session, "prefix"); + _print_json_netaddr(session, "destination", &lan->prefix.dst); + if (netaddr_get_prefix_length(&lan->prefix.src) > 0) { + _print_json_netaddr(session, "source", &lan->prefix.src); + } + json_end_object(session); + json_end_object(session); + } + } +} + static void _create_id_json(struct json_session *session) { struct olsrv2_tc_node *tc_node; struct olsrv2_tc_attachment *tc_attached; - struct olsrv2_lan_entry *lan; - struct nhdp_domain *domain; struct _node_id_str node_id_str; - int af; json_start_object(session, NULL); @@ -934,59 +959,256 @@ _create_id_json(struct json_session *session) { json_start_array(session, "routers"); - /* local router */ - if (olsrv2_originator_get(AF_INET)) { + avl_for_each_element(olsrv2_tc_get_tree(), tc_node, _originator_node) { json_start_object(session, NULL); - _print_json_string(session, "router_id", _get_node_id_me(&node_id_str, AF_INET)); - _print_json_netaddr(session, "router_addr", olsrv2_originator_get(AF_INET)); + _print_json_string(session, "id", _get_tc_node_id(&node_id_str, tc_node)); + + json_start_array(session, "addresses"); + _print_json_netaddr(session, NULL, &tc_node->target.prefix.dst); + json_end_array(session); + + json_start_array(session, "attached_prefixes"); + + /* locally attached */ + if (olsrv2_originator_is_local(&tc_node->target.prefix.dst)) { + _create_local_attached_ids(session, &tc_node->target.prefix.dst); + } + /* remote attached prefix*/ + avl_for_each_element(&tc_node->_attached_networks, tc_attached, _src_node) { + json_start_object(session, NULL); + _print_json_string(session, "id", _get_tc_endpoint_id(&node_id_str, tc_attached)); + json_start_array(session, "prefixes"); + json_start_object(session, NULL); + _print_json_netaddr(session, "destination", &tc_attached->dst->target.prefix.dst); + if (netaddr_get_prefix_length(&tc_attached->dst->target.prefix.src) > 0) { + _print_json_netaddr(session, "source", &tc_attached->dst->target.prefix.src); + } + json_end_object(session); + json_end_array(session); + json_end_object(session); + } + json_end_array(session); json_end_object(session); } - if (olsrv2_originator_get(AF_INET6)) { - json_start_object(session, NULL); - _print_json_string(session, "router_id", _get_node_id_me(&node_id_str, AF_INET6)); - _print_json_netaddr(session, "router_addr", olsrv2_originator_get(AF_INET6)); - json_end_object(session); + json_end_array(session); + json_end_object(session); +} + +static void +_print_layer2_data(struct json_session *session, const struct oonf_layer2_data *l2data, + const struct oonf_layer2_metadata *l2metadata) { + char value_buffer[64]; + + switch (l2metadata->type) { + case OONF_LAYER2_INTEGER_DATA: + _print_json_number(session, l2metadata->key, + oonf_layer2_data_to_string(value_buffer, sizeof(value_buffer), l2data, l2metadata, true)); + break; + case OONF_LAYER2_BOOLEAN_DATA: + _print_json_bool(session, l2metadata->key, l2data->_value.boolean); + break; + case OONF_LAYER2_DATA_TYPE_COUNT: + _print_json_netaddr(session, l2metadata->key, &l2data->_value.addr); + break; + default: + break; } +} - /* remote routers */ - avl_for_each_element(olsrv2_tc_get_tree(), tc_node, _originator_node) { +static void +_print_layer2_neighbor(struct json_session *session, struct oonf_layer2_neigh *l2neigh) { + const struct oonf_layer2_metadata *l2metadata; + struct oonf_layer2_neighbor_address *l2naddr; + struct oonf_layer2_destination *l2dest; + struct oonf_layer2_data *l2data; + struct isonumber_str ibuf; + char hexbuf[64]; + enum oonf_layer2_neighbor_index neigh_idx; + + json_start_object(session, NULL); + + if (netaddr_cmp(&l2neigh->key.addr, &l2neigh->network->if_listener.data->mac) != 0) { + _print_json_netaddr(session, "radio_mac", &l2neigh->key.addr); + } + if (strhex_from_bin(hexbuf, sizeof(hexbuf), + l2neigh->key.link_id, l2neigh->key.link_id_length) > 0) { + _print_json_string(session, "radio_link_id", hexbuf); + } + if (!avl_is_empty(&l2neigh->destinations)) { + json_start_array(session, "secondary_mac"); + avl_for_each_element(&l2neigh->destinations, l2dest, _node) { + _print_json_netaddr(session, NULL, &l2dest->destination); + } + json_end_array(session); + } + + if (!avl_is_empty(&l2neigh->remote_neighbor_ips)) { + json_start_array(session, "secondary_ip"); + avl_for_each_element(&l2neigh->remote_neighbor_ips, l2naddr, _neigh_node) { + _print_json_netaddr(session, NULL, &l2naddr->ip); + } + json_end_array(session); + } + + _print_json_string(session, "last_seen", + oonf_clock_toIntervalString(&ibuf, + -oonf_clock_get_relative(oonf_layer2_neigh_get_lastseen(l2neigh)))); + + json_start_object(session, "neighbor_data"); + for (neigh_idx=0; neigh_idx < OONF_LAYER2_NEIGH_COUNT; neigh_idx++) { + l2data = &l2neigh->data[neigh_idx]; + l2metadata = oonf_layer2_neigh_metadata_get(neigh_idx); + + if (!oonf_layer2_data_has_value(l2data)) { + continue; + } + _print_layer2_data(session, l2data, l2metadata); + } + json_end_object(session); + json_end_object(session); +} + +static void +_print_rf_band(struct json_session *session, int64_t f, int64_t b, const char *mode) { + if (f > 0 || b > 0) { json_start_object(session, NULL); - _print_json_string(session, "router_id", _get_tc_node_id(&node_id_str, tc_node)); - _print_json_netaddr(session, "router_addr", &tc_node->target.prefix.dst); - json_end_object(session); + if (f > 0) { + _print_json_integer(session, "center", f); + } + if (b > 0) { + _print_json_integer(session, "badnwidth", b); + } + if (mode) { + _print_json_string(session, "mode", mode); + } } - json_end_array(session); +} - json_start_array(session, "attached"); - /* locally attached */ - avl_for_each_element(olsrv2_lan_get_tree(), lan, _node) { - af = netaddr_get_address_family(&lan->prefix.dst); - list_for_each_element(nhdp_domain_get_list(), domain, _node) { - json_start_object(session, NULL); - _print_json_string(session, "attached_id", _get_tc_lan_id(&node_id_str, lan)); - _print_json_string(session, "router_id", _get_node_id_me(&node_id_str, af)); - _print_json_netaddr(session, "router_addr", olsrv2_originator_get(af)); - _print_json_netaddr(session, "attached_dst", &lan->prefix.dst); - _print_json_netaddr(session, "attached_src", &lan->prefix.src); - json_end_object(session); +static void +_print_link_network(struct json_session *session, struct oonf_layer2_net *l2net) { + const struct oonf_layer2_metadata *l2metadata; + struct oonf_layer2_peer_address *l2peer; + struct oonf_layer2_data *l2data; + struct oonf_layer2_neigh *l2neigh; + struct os_interface_ip *os_ip; + struct os_interface *os_if; + struct isonumber_str ibuf; + struct netaddr_str nbuf; + struct _node_id_str idbuf; + bool is_updown; + int64_t f1, f2, b1, b2; + enum oonf_layer2_network_index net_idx; + enum oonf_layer2_neighbor_index neigh_idx; + + os_if = l2net->if_listener.data; + + json_start_object(session, NULL); + + _print_json_string(session, "type", "NetworkLink"); + _print_json_string(session, "protocol", "olsrv2"); + _print_json_string(session, "version", oonf_log_get_libdata()->version); + _print_json_string(session, "revision", oonf_log_get_libdata()->git_commit); + + _print_json_string(session, "router_id", _get_node_id_me(&idbuf, AF_INET)); + _print_json_string(session, "interface_name", l2net->name); + if (l2net->if_type != OONF_LAYER2_TYPE_UNDEFINED) { + _print_json_string(session, "interface_type", + oonf_layer2_net_get_type_name(l2net->if_type)); + } + if (!netaddr_is_unspec(&os_if->mac)) { + _print_json_string(session, "interface_mac", netaddr_to_string(&nbuf, &os_if->mac)); + } + if (!avl_is_empty(&os_if->addresses)) { + json_start_array(session, "interface_ip"); + avl_for_each_element(&os_if->addresses, os_ip, _node) { + _print_json_string(session, NULL, netaddr_to_string(&nbuf, &os_ip->prefixed_addr)); } + json_end_array(session); } - /* remote prefixes */ - avl_for_each_element(olsrv2_tc_get_tree(), tc_node, _originator_node) { - avl_for_each_element(&tc_node->_attached_networks, tc_attached, _src_node) { - json_start_object(session, NULL); - _print_json_string(session, "attached_id", _get_tc_endpoint_id(&node_id_str, tc_attached)); - _print_json_string(session, "router_id", _get_tc_node_id(&node_id_str, tc_node)); - _print_json_netaddr(session, "router_addr", &tc_node->target.prefix.dst); - _print_json_netaddr(session, "attached_dst", &tc_attached->dst->target.prefix.dst); - _print_json_netaddr(session, "attached_src", &tc_attached->dst->target.prefix.src); - json_end_object(session); + _print_json_bool(session, "dlep", l2net->if_dlep); + _print_json_string(session, "last_seen", + oonf_clock_toIntervalString(&ibuf, -oonf_clock_get_relative(l2net->last_seen))); + + if (!avl_is_empty(&l2net->local_peer_ips)) { + json_start_array(session, "local_radio_ip"); + + avl_for_each_element(&l2net->local_peer_ips, l2peer, _net_node) { + _print_json_string(session, NULL, netaddr_to_string(&nbuf, &l2peer->ip)); + } + json_end_array(session); + } + + f1 = 0; + f2 = 0; + b1 = 0; + b2 = 0; + is_updown = false; + json_start_object(session, "interface_data"); + for (net_idx=0; net_idx < OONF_LAYER2_NET_COUNT; net_idx++) { + l2data = &l2net->data[net_idx]; + l2metadata = oonf_layer2_net_metadata_get(net_idx); + + if (!oonf_layer2_data_has_value(l2data)) { + continue; + } + if (net_idx == OONF_LAYER2_NET_BANDWIDTH_1) { + b1 = oonf_layer2_data_get_int64(l2data, l2metadata->scaling, 0); + } + else if (net_idx == OONF_LAYER2_NET_BANDWIDTH_2) { + b2 = oonf_layer2_data_get_int64(l2data, l2metadata->scaling, 0); + } + else if (net_idx == OONF_LAYER2_NET_FREQUENCY_1) { + f1 = oonf_layer2_data_get_int64(l2data, l2metadata->scaling, 0); + } + else if (net_idx == OONF_LAYER2_NET_FREQUENCY_2) { + f2 = oonf_layer2_data_get_int64(l2data, l2metadata->scaling, 0); + } + else if (net_idx == OONF_LAYER2_NET_BAND_UP_DOWN) { + is_updown = true; + } + else { + _print_layer2_data(session, l2data, l2metadata); + } + } + + if (f1 > 0 || f2 > 0 || b1 > 0 || b2 > 0) { + json_start_array(session, "rf_band"); + _print_rf_band(session, f1, b1, is_updown ? "tx" : NULL); + _print_rf_band(session, f2, b2, is_updown ? "rx" : NULL); + json_end_array(session); + } + json_end_object(session); + + json_start_object(session, "neighbor_defaults"); + for (neigh_idx=0; neigh_idx < OONF_LAYER2_NEIGH_COUNT; neigh_idx++) { + l2data = &l2net->neighdata[neigh_idx]; + l2metadata = oonf_layer2_neigh_metadata_get(neigh_idx); + + if (!oonf_layer2_data_has_value(l2data)) { + continue; } + _print_layer2_data(session, l2data, l2metadata); + } + json_end_object(session); + + json_start_array(session, "neighbors"); + avl_for_each_element(&l2net->neighbors, l2neigh, _node) { + _print_layer2_neighbor(session, l2neigh); } json_end_array(session); + json_end_object(session); } +static void +_create_link_json(struct json_session *session) { + struct oonf_layer2_net *l2net; + + avl_for_each_element(oonf_layer2_get_net_tree(), l2net, _node) { + _print_link_network(session, l2net); + } +} + /** * Print a JSON error * @param session json session @@ -1020,6 +1242,9 @@ _handle_netjson_object(struct json_session *session, const char *parameter, bool else if (!filter && (ptr = str_hasnextword(parameter, JSON_NAME_ID))) { _create_id_json(session); } + else if (!filter && (ptr = str_hasnextword(parameter, JSON_NAME_LINK))) { + _create_link_json(session); + } else { ptr = str_skipnextword(parameter); *error = true; @@ -1105,19 +1330,41 @@ _print_json_string(struct json_session *session, const char *key, const char *va } /** - * Helper to print a json number + * Helper to print a json boolean + * @param session json session + * @param key json key + * @param value boolean value + */ +static void +_print_json_bool(struct json_session *session, const char *key, bool value) { + json_print(session, key, false, json_getbool(value)); +} + +/** + * Helper to print a json integer * @param session json session * @param key json key * @param value number */ static void -_print_json_number(struct json_session *session, const char *key, uint64_t value) { +_print_json_integer(struct json_session *session, const char *key, uint64_t value) { char buffer[21]; snprintf(buffer, sizeof(buffer), "%" PRIu64, value); json_print(session, key, false, buffer); } +/** + * Helper to print a json number + * @param session json session + * @param key json key + * @param value number (as a string) + */ +static void +_print_json_number(struct json_session *session, const char *key, const char *value) { + json_print(session, key, false, value); +} + /** * Helper function to print a json netaddr object * @param session json session From 506c7b8ecd317da424f2183b12982e11e42da17d Mon Sep 17 00:00:00 2001 From: Henning Rogge Date: Tue, 16 Apr 2019 10:55:54 +0200 Subject: [PATCH 12/63] Fix data type of netjsoninfo --- src/olsrv2/netjsoninfo/netjsoninfo.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/olsrv2/netjsoninfo/netjsoninfo.c b/src/olsrv2/netjsoninfo/netjsoninfo.c index 0d3c400f..71422c5a 100644 --- a/src/olsrv2/netjsoninfo/netjsoninfo.c +++ b/src/olsrv2/netjsoninfo/netjsoninfo.c @@ -493,7 +493,7 @@ _print_edge_links( _print_json_integer(session, "in_cost", cost); _print_json_string(session, "in_text", nhdp_domain_get_link_metric_value(&mbuf, domain, cost)); - _print_json_string(session, "outgoing_tree", json_getbool(outgoing && best_link == lnk)); + _print_json_bool(session, "outgoing_tree", outgoing && best_link == lnk); json_end_object(session); } @@ -539,7 +539,7 @@ _print_graph_edge(struct json_session *session, struct nhdp_domain *domain, cons _print_json_integer(session, "in_cost", in); _print_json_string(session, "in_text", nhdp_domain_get_link_metric_value(&mbuf, domain, in)); } - _print_json_string(session, "outgoing_tree", json_getbool(outgoing_tree)); + _print_json_bool(session, "outgoing_tree", outgoing_tree); if (src_addr) { _print_json_netaddr(session, "source_addr", src_addr); @@ -1050,7 +1050,7 @@ _print_layer2_neighbor(struct json_session *session, struct oonf_layer2_neigh *l json_end_array(session); } - _print_json_string(session, "last_seen", + _print_json_number(session, "last_seen", oonf_clock_toIntervalString(&ibuf, -oonf_clock_get_relative(oonf_layer2_neigh_get_lastseen(l2neigh)))); @@ -1126,7 +1126,7 @@ _print_link_network(struct json_session *session, struct oonf_layer2_net *l2net) json_end_array(session); } _print_json_bool(session, "dlep", l2net->if_dlep); - _print_json_string(session, "last_seen", + _print_json_number(session, "last_seen", oonf_clock_toIntervalString(&ibuf, -oonf_clock_get_relative(l2net->last_seen))); if (!avl_is_empty(&l2net->local_peer_ips)) { From ab24b06c15a62903ce9f32f8ae3677316ed47eaf Mon Sep 17 00:00:00 2001 From: Henning Rogge Date: Wed, 17 Apr 2019 10:01:23 +0200 Subject: [PATCH 13/63] Fix custom netjsoninfo extensions --- src/olsrv2/netjsoninfo/netjsoninfo.c | 51 ++-------------------------- 1 file changed, 3 insertions(+), 48 deletions(-) diff --git a/src/olsrv2/netjsoninfo/netjsoninfo.c b/src/olsrv2/netjsoninfo/netjsoninfo.c index 71422c5a..184fb01a 100644 --- a/src/olsrv2/netjsoninfo/netjsoninfo.c +++ b/src/olsrv2/netjsoninfo/netjsoninfo.c @@ -921,33 +921,9 @@ _create_domain_json(struct json_session *session) { json_end_object(session); } -static void -_create_local_attached_ids(struct json_session *session, - const struct netaddr *originator) { - struct olsrv2_lan_entry *lan; - struct _node_id_str node_id_str; - int af; - - af = netaddr_get_address_family(originator); - avl_for_each_element(olsrv2_lan_get_tree(), lan, _node) { - if (netaddr_get_address_family(&lan->prefix.dst) == af) { - json_start_object(session, NULL); - _print_json_string(session, "id", _get_tc_lan_id(&node_id_str, lan)); - json_start_object(session, "prefix"); - _print_json_netaddr(session, "destination", &lan->prefix.dst); - if (netaddr_get_prefix_length(&lan->prefix.src) > 0) { - _print_json_netaddr(session, "source", &lan->prefix.src); - } - json_end_object(session); - json_end_object(session); - } - } -} - static void _create_id_json(struct json_session *session) { struct olsrv2_tc_node *tc_node; - struct olsrv2_tc_attachment *tc_attached; struct _node_id_str node_id_str; json_start_object(session, NULL); @@ -957,37 +933,16 @@ _create_id_json(struct json_session *session) { _print_json_string(session, "version", oonf_log_get_libdata()->version); _print_json_string(session, "revision", oonf_log_get_libdata()->git_commit); - json_start_array(session, "routers"); + json_start_array(session, "nodes"); avl_for_each_element(olsrv2_tc_get_tree(), tc_node, _originator_node) { json_start_object(session, NULL); - _print_json_string(session, "id", _get_tc_node_id(&node_id_str, tc_node)); + _print_json_string(session, "netjson_id", _get_tc_node_id(&node_id_str, tc_node)); json_start_array(session, "addresses"); _print_json_netaddr(session, NULL, &tc_node->target.prefix.dst); json_end_array(session); - json_start_array(session, "attached_prefixes"); - - /* locally attached */ - if (olsrv2_originator_is_local(&tc_node->target.prefix.dst)) { - _create_local_attached_ids(session, &tc_node->target.prefix.dst); - } - /* remote attached prefix*/ - avl_for_each_element(&tc_node->_attached_networks, tc_attached, _src_node) { - json_start_object(session, NULL); - _print_json_string(session, "id", _get_tc_endpoint_id(&node_id_str, tc_attached)); - json_start_array(session, "prefixes"); - json_start_object(session, NULL); - _print_json_netaddr(session, "destination", &tc_attached->dst->target.prefix.dst); - if (netaddr_get_prefix_length(&tc_attached->dst->target.prefix.src) > 0) { - _print_json_netaddr(session, "source", &tc_attached->dst->target.prefix.src); - } - json_end_object(session); - json_end_array(session); - json_end_object(session); - } - json_end_array(session); json_end_object(session); } json_end_array(session); @@ -1076,7 +1031,7 @@ _print_rf_band(struct json_session *session, int64_t f, int64_t b, const char *m _print_json_integer(session, "center", f); } if (b > 0) { - _print_json_integer(session, "badnwidth", b); + _print_json_integer(session, "bandwidth", b); } if (mode) { _print_json_string(session, "mode", mode); From 7cbd134a2918993e7fc0ff6ecec1479de3b9e27d Mon Sep 17 00:00:00 2001 From: Henning Rogge Date: Wed, 17 Apr 2019 16:32:29 +0200 Subject: [PATCH 14/63] Simplify netjsoninfo id output --- src/olsrv2/netjsoninfo/netjsoninfo.c | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/src/olsrv2/netjsoninfo/netjsoninfo.c b/src/olsrv2/netjsoninfo/netjsoninfo.c index 184fb01a..18ef9b61 100644 --- a/src/olsrv2/netjsoninfo/netjsoninfo.c +++ b/src/olsrv2/netjsoninfo/netjsoninfo.c @@ -926,17 +926,14 @@ _create_id_json(struct json_session *session) { struct olsrv2_tc_node *tc_node; struct _node_id_str node_id_str; - json_start_object(session, NULL); - - _print_json_string(session, "type", "NetworkId"); - _print_json_string(session, "protocol", "olsrv2"); - _print_json_string(session, "version", oonf_log_get_libdata()->version); - _print_json_string(session, "revision", oonf_log_get_libdata()->git_commit); - - json_start_array(session, "nodes"); - avl_for_each_element(olsrv2_tc_get_tree(), tc_node, _originator_node) { json_start_object(session, NULL); + + _print_json_string(session, "type", "NetworkId"); + _print_json_string(session, "protocol", "olsrv2"); + _print_json_string(session, "version", oonf_log_get_libdata()->version); + _print_json_string(session, "revision", oonf_log_get_libdata()->git_commit); + _print_json_string(session, "netjson_id", _get_tc_node_id(&node_id_str, tc_node)); json_start_array(session, "addresses"); @@ -945,8 +942,6 @@ _create_id_json(struct json_session *session) { json_end_object(session); } - json_end_array(session); - json_end_object(session); } static void From bd940120f782a1a06ccab935bcf5869cbd1eb909 Mon Sep 17 00:00:00 2001 From: Henning Rogge Date: Thu, 25 Apr 2019 13:54:26 +0200 Subject: [PATCH 15/63] Add ACL setting to dlep_radio to fix TCP session problem because of wrong ACL default --- apps/dlep-router/CMakeLists.txt | 1 + src/generic/dlep/radio/dlep_radio.c | 2 ++ src/generic/dlep/radio/dlep_radio_interface.c | 4 ++-- src/generic/dlep/radio/dlep_radio_session.c | 5 +++++ src/generic/dlep/router/dlep_router_interface.c | 4 ++-- 5 files changed, 12 insertions(+), 4 deletions(-) diff --git a/apps/dlep-router/CMakeLists.txt b/apps/dlep-router/CMakeLists.txt index c182fc76..6847dee4 100644 --- a/apps/dlep-router/CMakeLists.txt +++ b/apps/dlep-router/CMakeLists.txt @@ -46,6 +46,7 @@ IF (NOT OONF_STATIC_PLUGINS) systeminfo dlep_router layer2_export + http ) ENDIF (NOT OONF_STATIC_PLUGINS) diff --git a/src/generic/dlep/radio/dlep_radio.c b/src/generic/dlep/radio/dlep_radio.c index f4868c3e..6cb3edf4 100644 --- a/src/generic/dlep/radio/dlep_radio.c +++ b/src/generic/dlep/radio/dlep_radio.c @@ -100,6 +100,8 @@ static struct cfg_schema_entry _radio_entries[] = { "Server port for DLEP tcp sessions", 0, 1, 65535), CFG_MAP_ACL_V46(dlep_radio_if, tcp_config.bindto, "session_bindto", "169.254.0.0/16\0fe80::/10", "Filter to determine the binding of the TCP server socket"), + CFG_MAP_ACL_V46(dlep_radio_if, tcp_config.acl, "session_acl", ACL_DEFAULT_ACCEPT, + "Filter which IPs are allowed to connect to the TCP server socket"), CFG_MAP_CLOCK_MINMAX(dlep_radio_if, interf.session.cfg.heartbeat_interval, "heartbeat_interval", "1.000", "Interval in seconds between two heartbeat signals", 1000, 65535 * 1000), diff --git a/src/generic/dlep/radio/dlep_radio_interface.c b/src/generic/dlep/radio/dlep_radio_interface.c index e6c89f8a..1d47b553 100644 --- a/src/generic/dlep/radio/dlep_radio_interface.c +++ b/src/generic/dlep/radio/dlep_radio_interface.c @@ -81,13 +81,13 @@ static struct oonf_class _interface_class = { static bool _shutting_down; static struct oonf_layer2_origin _l2_origin = { - .name = "dlep radio", + .name = "dlep_radio", .proactive = true, .priority = OONF_LAYER2_ORIGIN_RELIABLE, }; static struct oonf_layer2_origin _l2_default_origin = { - .name = "dlep radio defaults", + .name = "dlep_radio_defaults", .proactive = false, .priority = OONF_LAYER2_ORIGIN_DEFAULT, }; diff --git a/src/generic/dlep/radio/dlep_radio_session.c b/src/generic/dlep/radio/dlep_radio_session.c index 5199b03f..682bc62b 100644 --- a/src/generic/dlep/radio/dlep_radio_session.c +++ b/src/generic/dlep/radio/dlep_radio_session.c @@ -218,9 +218,14 @@ _cb_send_buffer(struct dlep_session *session, int af_family __attribute((unused) static void _cb_end_session(struct dlep_session *session) { struct dlep_radio_session *radio_session; +#ifdef OONF_LOG_DEBUG_INFO + struct netaddr_str nbuf; +#endif /* get pointer to radio interface */ radio_session = container_of(session, struct dlep_radio_session, session); + OONF_DEBUG(session->log_source, "Ending session to %s", + netaddr_socket_to_string(&nbuf, &radio_session->stream.remote_socket)); dlep_radio_remove_session(radio_session); } diff --git a/src/generic/dlep/router/dlep_router_interface.c b/src/generic/dlep/router/dlep_router_interface.c index 7f62fd4f..bac673f6 100644 --- a/src/generic/dlep/router/dlep_router_interface.c +++ b/src/generic/dlep/router/dlep_router_interface.c @@ -90,13 +90,13 @@ static struct oonf_class _router_if_class = { static bool _shutting_down; static struct oonf_layer2_origin _l2_origin = { - .name = "dlep router", + .name = "dlep_router", .proactive = true, .priority = OONF_LAYER2_ORIGIN_RELIABLE, }; static struct oonf_layer2_origin _l2_default_origin = { - .name = "dlep router defaults", + .name = "dlep_router_defaults", .proactive = false, .priority = OONF_LAYER2_ORIGIN_UNRELIABLE, }; From 35b3f24c863f3dbf1ec1d2357df01aa184802370 Mon Sep 17 00:00:00 2001 From: Henning Rogge Date: Fri, 10 May 2019 11:46:56 +0200 Subject: [PATCH 16/63] Add IP/Prefix type to layer2 data types and add a DNS interface type based on it. --- include/oonf/base/oonf_layer2.h | 88 +++++++++++++++++++++++++++ include/oonf/libcommon/netaddr.h | 16 +++++ src/base/oonf_layer2.c | 57 +++++++++++++++++- src/libcommon/netaddr.c | 100 +++++++++++++++++++++++++++++++ 4 files changed, 260 insertions(+), 1 deletion(-) diff --git a/include/oonf/base/oonf_layer2.h b/include/oonf/base/oonf_layer2.h index a5a6a9ef..7f1eac33 100644 --- a/include/oonf/base/oonf_layer2.h +++ b/include/oonf/base/oonf_layer2.h @@ -216,6 +216,7 @@ enum oonf_layer2_data_type OONF_LAYER2_INTEGER_DATA, OONF_LAYER2_BOOLEAN_DATA, OONF_LAYER2_NETWORK_DATA, + OONF_LAYER2_SOCKET_DATA, OONF_LAYER2_DATA_TYPE_COUNT, }; @@ -224,6 +225,14 @@ union oonf_layer2_value { int64_t integer; bool boolean; struct netaddr addr; + union netaddr_socket socket; +}; + +enum oonf_layer2_network_flags { + OONF_LAYER2_IPV4_DATA = 1<<0, + OONF_LAYER2_IPV6_DATA = 1<<1, + OONF_LAYER2_UNSPEC_DATA = 1<<2, + OONF_LAYER2_HOST_ONLY_DATA = 1<<3, }; /** @@ -241,6 +250,9 @@ struct oonf_layer2_metadata { /*! scaling factor for */ const uint64_t scaling; + + /*! flags for network and socket datatype */ + enum oonf_layer2_network_flags net_flags; }; /** @@ -330,6 +342,18 @@ enum oonf_layer2_network_index */ OONF_LAYER2_NET_BAND_UP_DOWN, + /*! known local IPv4 DNS server */ + OONF_LAYER2_NET_IPV4_LOCAL_DNS, + + /*! known local IPv6 DNS server */ + OONF_LAYER2_NET_IPV6_LOCAL_DNS, + + /*! known remote IPv4 DNS server */ + OONF_LAYER2_NET_IPV4_REMOTE_DNS, + + /*! known remote IPv6 DNS server */ + OONF_LAYER2_NET_IPV6_REMOTE_DNS, + /*! number of layer2 network metrics */ OONF_LAYER2_NET_COUNT, }; @@ -468,6 +492,12 @@ struct oonf_layer2_net { /*! absolute timestamp when network has been active last */ uint64_t last_seen; + /*! + * DNS delivered that can be used for this interface, e.g. + * for DNS service discovery + */ + union netaddr_socket dns; + /*! network wide layer 2 data */ struct oonf_layer2_data data[OONF_LAYER2_NET_COUNT]; @@ -894,6 +924,64 @@ oonf_layer2_data_read_boolean(bool *buffer, const struct oonf_layer2_data *l2dat return 0; } +static INLINE bool +oonf_layer2_data_netaddr_is_unspec(const struct oonf_layer2_data *l2data) { + return !l2data->_meta + || oonf_layer2_data_get_type(l2data) != OONF_LAYER2_NETWORK_DATA + || netaddr_is_unspec(&l2data->_value.addr); +} + +/** + * @param l2data layer-2 data object + * @return pointer to netaddr object, NULL if not available + */ +static INLINE const struct netaddr * +oonf_layer2_data_get_netaddr(const struct oonf_layer2_data *l2data) { + if (!l2data->_meta || oonf_layer2_data_get_type(l2data) != OONF_LAYER2_NETWORK_DATA) { + return NULL; + } + return &l2data->_value.addr; +} + +static INLINE bool +oonf_layer2_data_socket_is_unspec(const struct oonf_layer2_data *l2data) { + return !l2data->_meta + || oonf_layer2_data_get_type(l2data) != OONF_LAYER2_SOCKET_DATA + || netaddr_is_unspec(&l2data->_value.addr); +} + +/** + * @param l2data layer-2 data object + * @return pointer to netaddr socket object, NULL if not available + */ +static INLINE const union netaddr_socket * +oonf_layer2_data_get_socket(const struct oonf_layer2_data *l2data) { + if (!l2data->_meta || oonf_layer2_data_get_type(l2data) != OONF_LAYER2_SOCKET_DATA) { + return NULL; + } + return &l2data->_value.socket; +} + +static INLINE bool +oonf_layer2_data_set_netaddr(struct oonf_layer2_data *l2data, const struct oonf_layer2_origin *origin, + const struct oonf_layer2_metadata *meta, const struct netaddr *addr) { + const union oonf_layer2_value *value = (union oonf_layer2_value *)addr; + if (l2data->_meta != NULL && l2data->_meta != meta) { + return false; + } + return oonf_layer2_data_set(l2data, origin, meta, value); +} + +static INLINE bool +oonf_layer2_data_set_socket(struct oonf_layer2_data *l2data, const struct oonf_layer2_origin *origin, + const struct oonf_layer2_metadata *meta, const union netaddr_socket *sock) { + const union oonf_layer2_value *value = (union oonf_layer2_value *)sock; + if (l2data->_meta != NULL && l2data->_meta != meta) { + return false; + } + return oonf_layer2_data_set(l2data, origin, meta, value); +} + /** * @param l2data layer-2 data object * @param def default value to return diff --git a/include/oonf/libcommon/netaddr.h b/include/oonf/libcommon/netaddr.h index 53eb81e8..3abb5c09 100644 --- a/include/oonf/libcommon/netaddr.h +++ b/include/oonf/libcommon/netaddr.h @@ -96,6 +96,9 @@ enum /*! text name for IPv6 unique local prefix */ #define NETADDR_STR_ULA "ula" +/*! compile constant initializer for netaddr unspec socket */ +#define NETADDR_SOCKET_UNSPEC_INIT { .std = { .sa_family = AF_UNSPEC } } + /** * Representation of an address including address type * At the moment we support AF_INET, AF_INET6 and AF_MAC48 @@ -177,6 +180,7 @@ EXPORT extern const struct netaddr NETADDR_MAC48_IPV6_MULTICAST; EXPORT extern const union netaddr_socket NETADDR_SOCKET_IPV4_ANY; EXPORT extern const union netaddr_socket NETADDR_SOCKET_IPV6_ANY; +EXPORT extern const union netaddr_socket NETADDR_SOCKET_UNSPEC; EXPORT int netaddr_from_binary_prefix( struct netaddr *dst, const void *binary, size_t len, uint8_t addr_type, uint8_t prefix_len); @@ -192,10 +196,12 @@ EXPORT void netaddr_truncate(struct netaddr *dst, const struct netaddr *src); EXPORT int netaddr_socket_init( union netaddr_socket *combined, const struct netaddr *addr, uint16_t port, unsigned if_index); +EXPORT const uint8_t *netaddr_socket_get_addr_binptr(const union netaddr_socket *sock); EXPORT uint16_t netaddr_socket_get_port(const union netaddr_socket *sock); EXPORT const char *netaddr_to_prefixstring(struct netaddr_str *dst, const struct netaddr *src, bool forceprefix); EXPORT int netaddr_from_string(struct netaddr *, const char *) __attribute__((warn_unused_result)); EXPORT const char *netaddr_socket_to_string(struct netaddr_str *, const union netaddr_socket *); +EXPORT int netaddr_socket_from_string(union netaddr_socket *, const char *); EXPORT int netaddr_cmp_to_socket(const struct netaddr *, const union netaddr_socket *); EXPORT bool netaddr_isequal_binary( @@ -442,4 +448,14 @@ netaddr_socket_get_addressfamily(const union netaddr_socket *s) { return s->std.sa_family; } +static INLINE size_t +netaddr_socket_get_addr_binlength(const union netaddr_socket *sock) { + return netaddr_get_af_maxprefix(sock->std.sa_family) >> 3; +} + +static INLINE unsigned +netaddr_socket_get_scopex(const union netaddr_socket *sock) { + return netaddr_socket_get_addressfamily(sock) == AF_INET6 ? sock->v6.sin6_scope_id : 0; +} + #endif /* NETADDR_H_ */ diff --git a/src/base/oonf_layer2.c b/src/base/oonf_layer2.c index 88561314..e03151af 100644 --- a/src/base/oonf_layer2.c +++ b/src/base/oonf_layer2.c @@ -133,6 +133,10 @@ static const struct oonf_layer2_metadata _metadata_net[OONF_LAYER2_NET_COUNT] = [OONF_LAYER2_NET_TX_ONLY_UNICAST] = { .key = "tx_only_unicast", .type = OONF_LAYER2_BOOLEAN_DATA }, [OONF_LAYER2_NET_RADIO_MULTIHOP] = { .key = "radio_multihop", .type = OONF_LAYER2_BOOLEAN_DATA }, [OONF_LAYER2_NET_BAND_UP_DOWN] = { .key = "band_updown", .type = OONF_LAYER2_BOOLEAN_DATA }, + [OONF_LAYER2_NET_IPV4_LOCAL_DNS] = { .key = "local_ipv4_dns", .type = OONF_LAYER2_SOCKET_DATA, .net_flags = OONF_LAYER2_IPV4_DATA | OONF_LAYER2_UNSPEC_DATA }, + [OONF_LAYER2_NET_IPV6_LOCAL_DNS] = { .key = "local_ipv6_dns", .type = OONF_LAYER2_SOCKET_DATA, .net_flags = OONF_LAYER2_IPV6_DATA | OONF_LAYER2_UNSPEC_DATA }, + [OONF_LAYER2_NET_IPV4_REMOTE_DNS] = { .key = "remote_ipv4_dns", .type = OONF_LAYER2_SOCKET_DATA, .net_flags = OONF_LAYER2_IPV4_DATA | OONF_LAYER2_UNSPEC_DATA }, + [OONF_LAYER2_NET_IPV6_REMOTE_DNS] = { .key = "remote_ipv6_dns", .type = OONF_LAYER2_SOCKET_DATA, .net_flags = OONF_LAYER2_IPV6_DATA | OONF_LAYER2_UNSPEC_DATA }, }; static const char *_network_type[OONF_LAYER2_TYPE_COUNT] = { @@ -155,6 +159,7 @@ static const char *_data_types[OONF_LAYER2_DATA_TYPE_COUNT] = { [OONF_LAYER2_INTEGER_DATA] = "integer", [OONF_LAYER2_BOOLEAN_DATA] = "boolean", [OONF_LAYER2_NETWORK_DATA] = "network", + [OONF_LAYER2_SOCKET_DATA] = "socket", }; /* infrastructure for l2net/l2neigh tree */ @@ -291,11 +296,51 @@ oonf_layer2_data_parse_string( case OONF_LAYER2_BOOLEAN_DATA: if (!cfg_is_bool(input)) { - return -1; + return -2; } value->boolean = cfg_get_bool(input); return 0; + case OONF_LAYER2_NETWORK_DATA: + if (netaddr_from_string(&value->addr, input)) { + return -2; + } + if (netaddr_is_unspec(&value->addr) + && (meta->net_flags & OONF_LAYER2_UNSPEC_DATA) == 0) { + return -3; + } + if (netaddr_get_address_family(&value->addr) == AF_INET + && (meta->net_flags & OONF_LAYER2_IPV4_DATA) == 0) { + return -4; + } + if (netaddr_get_address_family(&value->addr) == AF_INET6 + && (meta->net_flags & OONF_LAYER2_IPV6_DATA) == 0) { + return -5; + } + if (!netaddr_is_host(&value->addr) + && (meta->net_flags & OONF_LAYER2_HOST_ONLY_DATA) != 0) { + return -6; + } + return 0; + + case OONF_LAYER2_SOCKET_DATA: + if (netaddr_socket_from_string(&value->socket, input)) { + return -2; + } + if (netaddr_socket_is_unspec(&value->socket) + && (meta->net_flags & OONF_LAYER2_UNSPEC_DATA) == 0) { + return -3; + } + if (netaddr_socket_get_addressfamily(&value->socket) == AF_INET + && (meta->net_flags & OONF_LAYER2_IPV4_DATA) == 0) { + return -4; + } + if (netaddr_socket_get_addressfamily(&value->socket) == AF_INET6 + && (meta->net_flags & OONF_LAYER2_IPV6_DATA) == 0) { + return -5; + } + return 0; + default: return -1; } @@ -314,6 +359,7 @@ const char * oonf_layer2_data_to_string( char *buffer, size_t length, const struct oonf_layer2_data *data, const struct oonf_layer2_metadata *meta, bool raw) { struct isonumber_str iso_str; + struct netaddr_str nbuf; switch (meta->type) { case OONF_LAYER2_INTEGER_DATA: @@ -325,6 +371,12 @@ oonf_layer2_data_to_string( case OONF_LAYER2_BOOLEAN_DATA: return strscpy(buffer, json_getbool(data->_value.boolean), length); + case OONF_LAYER2_NETWORK_DATA: + return strscpy(buffer, netaddr_to_string(&nbuf, &data->_value.addr), length); + + case OONF_LAYER2_SOCKET_DATA: + return strscpy(buffer, netaddr_socket_to_string(&nbuf, &data->_value.socket), length); + default: return NULL; } @@ -419,6 +471,9 @@ oonf_layer2_data_compare(const union oonf_layer2_value *left, const union oonf_l case OONF_LAYER2_NETWORK_DATA: result = memcmp(&left->addr, &right->addr, sizeof(left->addr)); break; + case OONF_LAYER2_SOCKET_DATA: + result = memcmp(&left->socket, &right->socket, sizeof(left->socket)); + break; default: return false; } diff --git a/src/libcommon/netaddr.c b/src/libcommon/netaddr.c index 077f387a..d8c5d02e 100644 --- a/src/libcommon/netaddr.c +++ b/src/libcommon/netaddr.c @@ -135,6 +135,9 @@ const union netaddr_socket NETADDR_SOCKET_IPV6_ANY = { .v6 = { .sin6_scope_id = 0, } }; + /*! socket for binding to any IPv6 address */ +const union netaddr_socket NETADDR_SOCKET_UNSPEC = NETADDR_SOCKET_UNSPEC_INIT; + /* List of predefined address prefixes */ static const struct { const char *name; @@ -494,6 +497,22 @@ netaddr_socket_init(union netaddr_socket *combined, const struct netaddr *addr, return 0; } +/** + * @param sock pointer to netaddr_socket + * @return pointer to binary address + */ +const uint8_t * +netaddr_socket_get_addr_binptr(const union netaddr_socket *sock) { + switch (sock->std.sa_family) { + case AF_INET: + return (uint8_t *)&sock->v4.sin_addr.s_addr; + case AF_INET6: + return (uint8_t *)&sock->v6.sin6_addr; + default: + return NULL; + } +} + /** * @param sock pointer to netaddr_socket * @return port of socket @@ -750,6 +769,87 @@ netaddr_socket_to_string(struct netaddr_str *dst, const union netaddr_socket *sr return dst->buf; } +/** + * Converts a socket (: or []: or []:%interface) + * to a network socket + * @param dst network socket object to store result + * @param src input string + * @return 0 if conversion was successful, negative otherwise + */ +int +netaddr_socket_from_string(union netaddr_socket *dst, const char *src) { + struct netaddr_str nbuf; + char *ptr1, *ptr2; + char *ip_start; + char *port_start; + char *if_start; + struct netaddr addr; + uint32_t port; + unsigned idx; + + /* copy address so we can modify the string */ + strscpy(nbuf.buf, src, sizeof(nbuf)); + + /* get rid of whitespaces */ + ptr1 = str_trim(nbuf.buf); + if (ptr1[0] == '-' && ptr1[1] == 0) { + netaddr_socket_invalidate(dst); + return 0; + } + + if (ptr1[0] == '[') { + ptr2 = strchr(ptr1, ']'); + if (!ptr2) { + return -1; + } + + /* get ip start */ + ip_start = &ptr1[1]; + *ptr2 = 0; + + ptr1 = &ptr2[1]; + } + else { + ip_start = ptr1; + ptr1 = strchr(ptr1, ':'); + if (!ptr1) { + return -2; + } + } + /* get port start */ + if (*ptr1 != ':') { + return -3; + } + *ptr1 = 0; + port_start = &ptr1[1]; + + /* get interface start */ + ptr2 = strchr(port_start, '%'); + if (ptr2) { + *ptr2 = 0; + if_start = &ptr2[1]; + } + else { + if_start = NULL; + } + + if (netaddr_from_string(&addr, ip_start)) { + return -4; + } + port = strtoul(port_start, NULL, 10); + if (port > 65535) { + return -5; + } + if (if_start) { + idx = if_nametoindex(if_start); + } + else { + idx = 0; + } + + return netaddr_socket_init(dst, &addr, port, idx) == 0 ? 0 : -6; +} + /** * Compares two addresses in network byte order. * Address type will be compared last. From f96f1821bf0711f42d29f6c074d6640dd56081ab Mon Sep 17 00:00:00 2001 From: Henning Rogge Date: Fri, 10 May 2019 11:50:53 +0200 Subject: [PATCH 17/63] Add DNS based service discovery --- apps/dlep-router/CMakeLists.txt | 8 +- include/oonf/base/oonf_class.h | 4 - include/oonf/base/oonf_rfc5444.h | 12 + include/oonf/generic/dlep/dlep_extension.h | 8 +- include/oonf/generic/dlep/dlep_iana.h | 16 +- include/oonf/generic/dlep/dlep_reader.h | 4 + include/oonf/generic/dlep/dlep_writer.h | 2 + include/oonf/generic/dlep/ext_dns/dns.h | 55 + .../generic/dlep/radio/dlep_radio_interface.h | 6 + .../dlep/router/dlep_router_interface.h | 7 + include/oonf/generic/dns_query/dns.h | 1248 +++ include/oonf/generic/dns_query/dns_query.h | 103 + include/oonf/generic/dns_sd/dns_sd.h | 126 + include/oonf/generic/dns_sd/dns_sd_intern.h | 38 + include/oonf/olsrv2/olsrv2/olsrv2_tc.h | 4 +- src/base/oonf_packet_socket.c | 7 +- src/generic/CMakeLists.txt | 2 + src/generic/dlep/CMakeLists.txt | 3 +- src/generic/dlep/dlep_reader.c | 141 +- src/generic/dlep/dlep_writer.c | 176 +- src/generic/dlep/ext_dns/dns.c | 137 + .../dlep/ext_l1_statistics/l1_statistics.c | 4 - src/generic/dlep/radio/dlep_radio_interface.c | 30 +- .../dlep/router/dlep_router_interface.c | 27 +- src/generic/dns_query/CMakeLists.txt | 13 + src/generic/dns_query/dns.c | 9941 +++++++++++++++++ src/generic/dns_query/dns_query.c | 243 + src/generic/dns_sd/CMakeLists.txt | 5 + src/generic/dns_sd/dns_sd.c | 889 ++ src/generic/layer2_config/layer2_config.c | 22 +- 30 files changed, 13214 insertions(+), 67 deletions(-) create mode 100644 include/oonf/generic/dlep/ext_dns/dns.h create mode 100644 include/oonf/generic/dns_query/dns.h create mode 100644 include/oonf/generic/dns_query/dns_query.h create mode 100644 include/oonf/generic/dns_sd/dns_sd.h create mode 100644 include/oonf/generic/dns_sd/dns_sd_intern.h create mode 100644 src/generic/dlep/ext_dns/dns.c create mode 100644 src/generic/dns_query/CMakeLists.txt create mode 100644 src/generic/dns_query/dns.c create mode 100644 src/generic/dns_query/dns_query.c create mode 100644 src/generic/dns_sd/CMakeLists.txt create mode 100644 src/generic/dns_sd/dns_sd.c diff --git a/apps/dlep-router/CMakeLists.txt b/apps/dlep-router/CMakeLists.txt index 6847dee4..5292aeed 100644 --- a/apps/dlep-router/CMakeLists.txt +++ b/apps/dlep-router/CMakeLists.txt @@ -34,6 +34,7 @@ IF (NOT OONF_STATIC_PLUGINS) socket stream_socket telnet + http timer viewer os_clock @@ -44,9 +45,12 @@ IF (NOT OONF_STATIC_PLUGINS) cfg_compact layer2info systeminfo - dlep_router + remotecontrol + layer2_config layer2_export - http + dns_query + dns_sd + dlep_router ) ENDIF (NOT OONF_STATIC_PLUGINS) diff --git a/include/oonf/base/oonf_class.h b/include/oonf/base/oonf_class.h index 37379112..ad9667fe 100644 --- a/include/oonf/base/oonf_class.h +++ b/include/oonf/base/oonf_class.h @@ -232,11 +232,7 @@ oonf_class_get_extension(struct oonf_class_extension *ext, void *ptr) { /** * @param ext extension data structure * @param ptr pointer to extension block -<<<<<<< HEAD * @return pointer to base memory block -======= - * @return pointer to extensions base block ->>>>>>> mpr_rework */ static INLINE void * oonf_class_get_base(struct oonf_class_extension *ext, void *ptr) { diff --git a/include/oonf/base/oonf_rfc5444.h b/include/oonf/base/oonf_rfc5444.h index ae1d6429..e3e75d6c 100644 --- a/include/oonf/base/oonf_rfc5444.h +++ b/include/oonf/base/oonf_rfc5444.h @@ -359,6 +359,18 @@ oonf_rfc5444_is_target_active(struct oonf_rfc5444_target *target) { oonf_packet_managed_is_active(&target->interface->_socket, netaddr_get_address_family(&target->dst)); } +static INLINE bool +oonf_rfc5444_is_interface_routing(struct oonf_rfc5444_interface *interface) { + return !interface->_socket.config.dont_route; +} + +static INLINE void +oonf_rfc5444_set_interface_routing(struct oonf_rfc5444_interface *interface, bool route) { + if (oonf_rfc5444_is_interface_routing(interface) != route) { + interface->_socket.config.dont_route = !route; + oonf_rfc5444_reconfigure_interface(interface, NULL); + } +} /** * Request a protocol wide packet sequence number * @param protocol pointer to rfc5444 protocol instance diff --git a/include/oonf/generic/dlep/dlep_extension.h b/include/oonf/generic/dlep/dlep_extension.h index 4f47f891..1694af28 100644 --- a/include/oonf/generic/dlep/dlep_extension.h +++ b/include/oonf/generic/dlep/dlep_extension.h @@ -177,6 +177,9 @@ struct dlep_neighbor_mapping { /*! layer2 neighbor id */ enum oonf_layer2_neighbor_index layer2; + /*! layer2 neighbor id for DLEP to layer2 conversion (optional) */ + enum oonf_layer2_neighbor_index layer2_dst; + /*! TLV is mandatory */ bool mandatory; @@ -223,9 +226,12 @@ struct dlep_network_mapping { /* fixed integer arithmetics scaling factor */ uint64_t scaling; - /*! layer2 network index */ + /*! layer2 network id */ enum oonf_layer2_network_index layer2; + /*! layer2 network id for DLEP to layer2 conversion (optional) */ + enum oonf_layer2_network_index layer2_dst; + /*! TLV is mandatory */ bool mandatory; diff --git a/include/oonf/generic/dlep/dlep_iana.h b/include/oonf/generic/dlep/dlep_iana.h index 871a6f5e..98dc1889 100644 --- a/include/oonf/generic/dlep/dlep_iana.h +++ b/include/oonf/generic/dlep/dlep_iana.h @@ -105,8 +105,11 @@ enum dlep_extensions /*! DLEP Link ID */ DLEP_EXTENSION_LINK_ID = 65523, + /*! DNS server exchange */ + DLEP_EXTENSION_DNS = 65524, + /*! number of supported (non-base) DLEP extensions */ - DLEP_EXTENSION_COUNT = 4, + DLEP_EXTENSION_COUNT = 5, }; /** @@ -322,16 +325,23 @@ enum dlep_tlvs /*! rx broadcast bitrate */ DLEP_CDRR_BC_TLV, + + /* ipv4 DNS service */ + DLEP_IPV4_DNS_SERVER_TLV, + + /* ipv6 DNS service */ + DLEP_IPV6_DNS_SERVER_TLV, }; enum dlep_peer_type_flags { - /*! radio does not implement access control */ + /*! service does not implement access control, just use TCP */ DLEP_PEER_TYPE_OPEN = 0, - /*! radio does implement access control */ + /*! service does implement TLS access control */ DLEP_PEER_TYPE_SECURED = 1, }; + /** * Flags for IP address TLVs */ diff --git a/include/oonf/generic/dlep/dlep_reader.h b/include/oonf/generic/dlep/dlep_reader.h index a3041429..847f8eaf 100644 --- a/include/oonf/generic/dlep/dlep_reader.h +++ b/include/oonf/generic/dlep/dlep_reader.h @@ -71,6 +71,10 @@ int dlep_reader_ipv4_conpoint_tlv( struct netaddr *addr, uint16_t *port, bool *tls, struct dlep_session *session, struct dlep_parser_value *value); int dlep_reader_ipv6_conpoint_tlv( struct netaddr *addr, uint16_t *port, bool *tls, struct dlep_session *session, struct dlep_parser_value *value); +int dlep_reader_ipv4_dns_tlv( + union netaddr_socket *sock, struct dlep_session *session, struct dlep_parser_value *value); +int dlep_reader_ipv6_dns_tlv( + union netaddr_socket *sock, struct dlep_session *session, struct dlep_parser_value *value); int dlep_reader_uint64( uint64_t *number, uint16_t tlv_id, struct dlep_session *session, struct dlep_parser_value *value); int dlep_reader_int64(int64_t *number, uint16_t tlv_id, struct dlep_session *session, struct dlep_parser_value *value); diff --git a/include/oonf/generic/dlep/dlep_writer.h b/include/oonf/generic/dlep/dlep_writer.h index f6cb4a41..27d602ff 100644 --- a/include/oonf/generic/dlep/dlep_writer.h +++ b/include/oonf/generic/dlep/dlep_writer.h @@ -67,6 +67,8 @@ int dlep_writer_add_lid_length_tlv(struct dlep_writer *writer, uint16_t link_id_ int dlep_writer_add_ip_tlv(struct dlep_writer *writer, const struct netaddr *ipv4, bool add); void dlep_writer_add_ipv4_conpoint_tlv(struct dlep_writer *writer, const struct netaddr *addr, uint16_t port, bool tls); void dlep_writer_add_ipv6_conpoint_tlv(struct dlep_writer *writer, const struct netaddr *addr, uint16_t port, bool tls); +void dlep_writer_add_ipv4_dns_tlv(struct dlep_writer *writer, const union netaddr_socket *sock); +void dlep_writer_add_ipv6_dns_tlv(struct dlep_writer *writer, const union netaddr_socket *sock); void dlep_writer_add_uint64(struct dlep_writer *writer, uint64_t number, enum dlep_tlvs tlv); void dlep_writer_add_int64(struct dlep_writer *writer, int64_t number, enum dlep_tlvs tlv); int dlep_writer_add_status(struct dlep_writer *writer, enum dlep_status status, const char *text); diff --git a/include/oonf/generic/dlep/ext_dns/dns.h b/include/oonf/generic/dlep/ext_dns/dns.h new file mode 100644 index 00000000..d6d564f1 --- /dev/null +++ b/include/oonf/generic/dlep/ext_dns/dns.h @@ -0,0 +1,55 @@ + +/* + * The olsr.org Optimized Link-State Routing daemon version 2 (olsrd2) + * Copyright (c) 2004-2015, the olsr.org team - see HISTORY file + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of olsr.org, olsrd nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * Visit http://www.olsr.org for more information. + * + * If you find this software useful feel free to make a donation + * to the project. For more information see the website or contact + * the copyright holders. + * + */ + +/** + * @file + */ + +#ifndef _DLEP_DNS_H_ +#define _DLEP_DNS_H_ + +#include +#include + +struct dlep_extension *dlep_dns_init(void); +void dlep_dns_cleanup(void); + +#endif /* _DLEP_DNS_H_ */ diff --git a/include/oonf/generic/dlep/radio/dlep_radio_interface.h b/include/oonf/generic/dlep/radio/dlep_radio_interface.h index 8d14c3ad..6c3a6796 100644 --- a/include/oonf/generic/dlep/radio/dlep_radio_interface.h +++ b/include/oonf/generic/dlep/radio/dlep_radio_interface.h @@ -66,6 +66,12 @@ struct dlep_radio_if { /*! configuration of TCP socket */ struct oonf_stream_managed_config tcp_config; + + /* layer2 origin for this interface */ + struct oonf_layer2_origin l2_origin; + + /* layer2 origin for this interface default values*/ + struct oonf_layer2_origin l2_default_origin; }; int dlep_radio_interface_init(void); diff --git a/include/oonf/generic/dlep/router/dlep_router_interface.h b/include/oonf/generic/dlep/router/dlep_router_interface.h index c0a625b2..3ff704a4 100644 --- a/include/oonf/generic/dlep/router/dlep_router_interface.h +++ b/include/oonf/generic/dlep/router/dlep_router_interface.h @@ -48,6 +48,7 @@ #include #include +#include #include #include @@ -69,6 +70,12 @@ struct dlep_router_if { /*! TCP port to directly connect router to */ int32_t connect_to_port; + /* layer2 origin for this interface */ + struct oonf_layer2_origin l2_origin; + + /* layer2 origin for this interface default values*/ + struct oonf_layer2_origin l2_default_origin; + /* timer to make sure we stay connected */ struct oonf_timer_instance _connect_to_watchdog; }; diff --git a/include/oonf/generic/dns_query/dns.h b/include/oonf/generic/dns_query/dns.h new file mode 100644 index 00000000..8c6265c2 --- /dev/null +++ b/include/oonf/generic/dns_query/dns.h @@ -0,0 +1,1248 @@ +/* ========================================================================== + * dns.h - Recursive, Reentrant DNS Resolver. + * -------------------------------------------------------------------------- + * Copyright (c) 2009, 2010, 2012-2015 William Ahern + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to permit + * persons to whom the Software is furnished to do so, subject to the + * following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN + * NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE + * USE OR OTHER DEALINGS IN THE SOFTWARE. + * ========================================================================== + */ +#ifndef DNS_H +#define DNS_H + +#include /* size_t offsetof() */ +#include /* FILE */ + +#include /* strlen(3) */ + +#include /* time_t */ + +#ifdef _WIN32 +#include +#include +#else +#include /* BYTE_ORDER BIG_ENDIAN _BIG_ENDIAN */ +#include /* socklen_t */ +#include /* struct socket */ + +#include /* POLLIN POLLOUT */ + +#include /* struct in_addr struct in6_addr */ + +#include /* struct addrinfo */ +#endif + + +/* + * V I S I B I L I T Y + * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#ifndef DNS_PUBLIC +#define DNS_PUBLIC +#endif + + +/* + * V E R S I O N + * + * Vendor: Entity for which versions numbers are relevant. (If forking + * change DNS_VENDOR to avoid confusion.) + * + * Three versions: + * + * REL Official "release"--bug fixes, new features, etc. + * ABI Changes to existing object sizes or parameter types. + * API Changes that might effect application source. + * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#define DNS_VENDOR "william@25thandClement.com" + +#define DNS_V_REL 0x20161214 +#define DNS_V_ABI 0x20160608 +#define DNS_V_API 0x20160608 + + +DNS_PUBLIC const char *dns_vendor(void); + +DNS_PUBLIC int dns_v_rel(void); +DNS_PUBLIC int dns_v_abi(void); +DNS_PUBLIC int dns_v_api(void); + + +/* + * E R R O R S + * + * Errors and exceptions are always returned through an int. This should + * hopefully make integration easier in the majority of circumstances, and + * also cut down on useless compiler warnings. + * + * System and library errors are returned together. POSIX guarantees that + * all system errors are positive integers. Library errors are always + * negative integers in the range DNS_EBASE to DNS_ELAST, with the high bits + * set to the three magic ASCII characters "dns". + * + * dns_strerror() returns static English string descriptions of all known + * errors, and punts the remainder to strerror(3). + * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#define DNS_EBASE -(('d' << 24) | ('n' << 16) | ('s' << 8) | 64) + +#define dns_error_t int /* for documentation only */ + +enum dns_errno { + DNS_ENOBUFS = DNS_EBASE, + DNS_EILLEGAL, + DNS_EORDER, + DNS_ESECTION, + DNS_EUNKNOWN, + DNS_EADDRESS, + DNS_ENOQUERY, + DNS_ENOANSWER, + DNS_EFETCHED, + DNS_ESERVICE, /* EAI_SERVICE */ + DNS_ENONAME, /* EAI_NONAME */ + DNS_EFAIL, /* EAI_FAIL */ + DNS_ELAST, +}; /* dns_errno */ + +DNS_PUBLIC const char *dns_strerror(dns_error_t); + +DNS_PUBLIC int *dns_debug_p(void); + +#define dns_debug (*dns_debug_p()) /* was extern int dns_debug before 20160523 API */ + + +/* + * C O M P I L E R A N N O T A T I O N S + * + * GCC with -Wextra, and clang by default, complain about overrides in + * initializer lists. Overriding previous member initializers is well + * defined behavior in C. dns.c relies on this behavior to define default, + * overrideable member values when instantiating configuration objects. + * + * dns_quietinit() guards a compound literal expression with pragmas to + * silence these shrill warnings. This alleviates the burden of requiring + * third-party projects to adjust their compiler flags. + * + * NOTE: If you take the address of the compound literal, take the address + * of the transformed expression, otherwise the compound literal lifetime is + * tied to the scope of the GCC statement expression. + * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#if defined __clang__ +#define DNS_PRAGMA_PUSH _Pragma("clang diagnostic push") +#define DNS_PRAGMA_QUIET _Pragma("clang diagnostic ignored \"-Winitializer-overrides\"") +#define DNS_PRAGMA_POP _Pragma("clang diagnostic pop") + +#define dns_quietinit(...) \ + DNS_PRAGMA_PUSH DNS_PRAGMA_QUIET __VA_ARGS__ DNS_PRAGMA_POP +#elif (__GNUC__ == 4 && __GNUC_MINOR__ >= 6) || __GNUC__ > 4 +#define DNS_PRAGMA_PUSH _Pragma("GCC diagnostic push") +#define DNS_PRAGMA_QUIET _Pragma("GCC diagnostic ignored \"-Woverride-init\"") +#define DNS_PRAGMA_POP _Pragma("GCC diagnostic pop") + +/* GCC parses the _Pragma operator less elegantly than clang. */ +#define dns_quietinit(...) \ + __extension__ ({ DNS_PRAGMA_PUSH DNS_PRAGMA_QUIET __VA_ARGS__; DNS_PRAGMA_POP }) +#else +#define DNS_PRAGMA_PUSH +#define DNS_PRAGMA_QUIET +#define DNS_PRAGMA_POP +#define dns_quietinit(...) __VA_ARGS__ +#endif + +#if defined __GNUC__ +#define DNS_PRAGMA_EXTENSION __extension__ +#else +#define DNS_PRAGMA_EXTENSION +#endif + + +/* + * E V E N T S I N T E R F A C E S + * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#if defined(POLLIN) +#define DNS_POLLIN POLLIN +#else +#define DNS_POLLIN 1 +#endif + +#if defined(POLLOUT) +#define DNS_POLLOUT POLLOUT +#else +#define DNS_POLLOUT 2 +#endif + + +/* + * See Application Interface below for configuring libevent bitmasks instead + * of poll(2) bitmasks. + */ +#define DNS_EVREAD 2 +#define DNS_EVWRITE 4 + + +#define DNS_POLL2EV(set) \ + (((set) & DNS_POLLIN)? DNS_EVREAD : 0) | (((set) & DNS_POLLOUT)? DNS_EVWRITE : 0) + +#define DNS_EV2POLL(set) \ + (((set) & DNS_EVREAD)? DNS_POLLIN : 0) | (((set) & DNS_EVWRITE)? DNS_POLLOUT : 0) + + +/* + * E N U M E R A T I O N I N T E R F A C E S + * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +enum dns_section { + DNS_S_QD = 0x01, +#define DNS_S_QUESTION DNS_S_QD + + DNS_S_AN = 0x02, +#define DNS_S_ANSWER DNS_S_AN + + DNS_S_NS = 0x04, +#define DNS_S_AUTHORITY DNS_S_NS + + DNS_S_AR = 0x08, +#define DNS_S_ADDITIONAL DNS_S_AR + + DNS_S_ALL = 0x0f +}; /* enum dns_section */ + + +enum dns_class { + DNS_C_IN = 1, + + DNS_C_ANY = 255 +}; /* enum dns_class */ + + +enum dns_type { + DNS_T_A = 1, + DNS_T_NS = 2, + DNS_T_CNAME = 5, + DNS_T_SOA = 6, + DNS_T_PTR = 12, + DNS_T_MX = 15, + DNS_T_TXT = 16, + DNS_T_AAAA = 28, + DNS_T_SRV = 33, + DNS_T_OPT = 41, + DNS_T_SSHFP = 44, + DNS_T_SPF = 99, + DNS_T_AXFR = 252, + + DNS_T_ALL = 255 +}; /* enum dns_type */ + + +enum dns_opcode { + DNS_OP_QUERY = 0, + DNS_OP_IQUERY = 1, + DNS_OP_STATUS = 2, + DNS_OP_NOTIFY = 4, + DNS_OP_UPDATE = 5, +}; /* dns_opcode */ + + +enum dns_rcode { + DNS_RC_NOERROR = 0, + DNS_RC_FORMERR = 1, + DNS_RC_SERVFAIL = 2, + DNS_RC_NXDOMAIN = 3, + DNS_RC_NOTIMP = 4, + DNS_RC_REFUSED = 5, + DNS_RC_YXDOMAIN = 6, + DNS_RC_YXRRSET = 7, + DNS_RC_NXRRSET = 8, + DNS_RC_NOTAUTH = 9, + DNS_RC_NOTZONE = 10, + + /* EDNS(0) extended RCODEs */ + DNS_RC_BADVERS = 16, +}; /* dns_rcode */ + + +/* + * NOTE: These string functions need a small buffer in case the literal + * integer value needs to be printed and returned. UNLESS this buffer is + * SPECIFIED, the returned string has ONLY BLOCK SCOPE. + */ +#define DNS_STRMAXLEN 47 /* "QUESTION|ANSWER|AUTHORITY|ADDITIONAL" */ + +DNS_PUBLIC const char *dns_strsection(enum dns_section, void *, size_t); +#define dns_strsection3(a, b, c) \ + dns_strsection((a), (b), (c)) +#define dns_strsection1(a) dns_strsection((a), (char [DNS_STRMAXLEN + 1]){ 0 }, DNS_STRMAXLEN + 1) +#define dns_strsection(...) DNS_PP_CALL(DNS_PP_XPASTE(dns_strsection, DNS_PP_NARG(__VA_ARGS__)), __VA_ARGS__) + +DNS_PUBLIC enum dns_section dns_isection(const char *); + +DNS_PUBLIC const char *dns_strclass(enum dns_class, void *, size_t); +#define dns_strclass3(a, b, c) dns_strclass((a), (b), (c)) +#define dns_strclass1(a) dns_strclass((a), (char [DNS_STRMAXLEN + 1]){ 0 }, DNS_STRMAXLEN + 1) +#define dns_strclass(...) DNS_PP_CALL(DNS_PP_XPASTE(dns_strclass, DNS_PP_NARG(__VA_ARGS__)), __VA_ARGS__) + +DNS_PUBLIC enum dns_class dns_iclass(const char *); + +DNS_PUBLIC const char *dns_strtype(enum dns_type, void *, size_t); +#define dns_strtype3(a, b, c) dns_strtype((a), (b), (c)) +#define dns_strtype1(a) dns_strtype((a), (char [DNS_STRMAXLEN + 1]){ 0 }, DNS_STRMAXLEN + 1) +#define dns_strtype(...) DNS_PP_CALL(DNS_PP_XPASTE(dns_strtype, DNS_PP_NARG(__VA_ARGS__)), __VA_ARGS__) + +DNS_PUBLIC enum dns_type dns_itype(const char *); + +DNS_PUBLIC const char *dns_stropcode(enum dns_opcode); + +DNS_PUBLIC enum dns_opcode dns_iopcode(const char *); + +DNS_PUBLIC const char *dns_strrcode(enum dns_rcode); + +DNS_PUBLIC enum dns_rcode dns_ircode(const char *); + + +/* + * A T O M I C I N T E R F A C E S + * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +typedef unsigned long dns_atomic_t; + +typedef unsigned long dns_refcount_t; /* must be same value type as dns_atomic_t */ + + +/* + * C R Y P T O I N T E R F A C E S + * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +typedef unsigned dns_random_f(void); + +DNS_PUBLIC dns_random_f **dns_random_p(void); + +#define dns_random (*dns_random_p()) /* was extern unsigned (*dns_random)(void) before 20160523 API */ + + +/* + * P A C K E T I N T E R F A C E + * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +struct dns_header { + unsigned qid:16; + +#if (defined BYTE_ORDER && BYTE_ORDER == BIG_ENDIAN) || (defined __sun && defined _BIG_ENDIAN) + unsigned qr:1; + unsigned opcode:4; + unsigned aa:1; + unsigned tc:1; + unsigned rd:1; + + unsigned ra:1; + unsigned unused:3; + unsigned rcode:4; +#else + unsigned rd:1; + unsigned tc:1; + unsigned aa:1; + unsigned opcode:4; + unsigned qr:1; + + unsigned rcode:4; + unsigned unused:3; + unsigned ra:1; +#endif + + unsigned qdcount:16; + unsigned ancount:16; + unsigned nscount:16; + unsigned arcount:16; +}; /* struct dns_header */ + +#define dns_header(p) (&(p)->header) + + +#ifndef DNS_P_QBUFSIZ +#define DNS_P_QBUFSIZ dns_p_calcsize(256 + 4) +#endif + +#ifndef DNS_P_DICTSIZE +#define DNS_P_DICTSIZE 16 +#endif + +struct dns_packet { + unsigned short dict[DNS_P_DICTSIZE]; + + struct dns_p_memo { + struct dns_s_memo { + unsigned short base, end; + } qd, an, ns, ar; + + struct { + unsigned short p; + unsigned short maxudp; + unsigned ttl; + } opt; + } memo; + + struct { struct dns_packet *cqe_next, *cqe_prev; } cqe; + + size_t size, end; + + int:16; /* tcp padding */ + + DNS_PRAGMA_EXTENSION union { + struct dns_header header; + unsigned char data[1]; + }; +}; /* struct dns_packet */ + +#define dns_p_calcsize(n) (offsetof(struct dns_packet, data) + DNS_PP_MAX(12, (n))) + +#define dns_p_sizeof(P) dns_p_calcsize((P)->end) + +/** takes size of maximum desired payload */ +#define dns_p_new(n) (dns_p_init((struct dns_packet *)&(union { unsigned char b[dns_p_calcsize((n))]; struct dns_packet p; }){ { 0 } }, dns_p_calcsize((n)))) + +/** takes size of entire packet structure as allocated */ +DNS_PUBLIC struct dns_packet *dns_p_init(struct dns_packet *, size_t); + +/** takes size of maximum desired payload */ +DNS_PUBLIC struct dns_packet *dns_p_make(size_t, int *); + +DNS_PUBLIC int dns_p_grow(struct dns_packet **); + +DNS_PUBLIC struct dns_packet *dns_p_copy(struct dns_packet *, const struct dns_packet *); + +#define dns_p_opcode(P) (dns_header(P)->opcode) + +DNS_PUBLIC enum dns_rcode dns_p_rcode(struct dns_packet *); + +DNS_PUBLIC unsigned dns_p_count(struct dns_packet *, enum dns_section); + +DNS_PUBLIC int dns_p_push(struct dns_packet *, enum dns_section, const void *, size_t, enum dns_type, enum dns_class, unsigned, const void *); + +DNS_PUBLIC void dns_p_dictadd(struct dns_packet *, unsigned short); + +DNS_PUBLIC struct dns_packet *dns_p_merge(struct dns_packet *, enum dns_section, struct dns_packet *, enum dns_section, int *); + +DNS_PUBLIC void dns_p_dump(struct dns_packet *, FILE *); + +DNS_PUBLIC int dns_p_study(struct dns_packet *); + + +/* + * D O M A I N N A M E I N T E R F A C E S + * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#define DNS_D_MAXLABEL 63 /* + 1 '\0' */ +#define DNS_D_MAXNAME 255 /* + 1 '\0' */ + +#define DNS_D_ANCHOR 1 /* anchor domain w/ root "." */ +#define DNS_D_CLEAVE 2 /* cleave sub-domain */ +#define DNS_D_TRIM 4 /* remove superfluous dots */ + +#define dns_d_new3(a, b, f) dns_d_init(&(char[DNS_D_MAXNAME + 1]){ 0 }, DNS_D_MAXNAME + 1, (a), (b), (f)) +#define dns_d_new2(a, f) dns_d_new3((a), strlen((a)), (f)) +#define dns_d_new1(a) dns_d_new3((a), strlen((a)), DNS_D_ANCHOR) +#define dns_d_new(...) DNS_PP_CALL(DNS_PP_XPASTE(dns_d_new, DNS_PP_NARG(__VA_ARGS__)), __VA_ARGS__) + +DNS_PUBLIC char *dns_d_init(void *, size_t, const void *, size_t, int); + +DNS_PUBLIC size_t dns_d_anchor(void *, size_t, const void *, size_t); + +DNS_PUBLIC size_t dns_d_cleave(void *, size_t, const void *, size_t); + +DNS_PUBLIC size_t dns_d_comp(void *, size_t, const void *, size_t, struct dns_packet *, int *); + +DNS_PUBLIC size_t dns_d_expand(void *, size_t, unsigned short, struct dns_packet *, int *); + +DNS_PUBLIC unsigned short dns_d_skip(unsigned short, struct dns_packet *); + +DNS_PUBLIC int dns_d_push(struct dns_packet *, const void *, size_t); + +DNS_PUBLIC size_t dns_d_cname(void *, size_t, const void *, size_t, struct dns_packet *, int *error); + + +/* + * R E S O U R C E R E C O R D I N T E R F A C E S + * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +struct dns_rr { + enum dns_section section; + + struct { + unsigned short p; + unsigned short len; + } dn; + + enum dns_type type; + enum dns_class class; + unsigned ttl; + + struct { + unsigned short p; + unsigned short len; + } rd; +}; /* struct dns_rr */ + + +DNS_PUBLIC int dns_rr_copy(struct dns_packet *, struct dns_rr *, struct dns_packet *); + +DNS_PUBLIC int dns_rr_parse(struct dns_rr *, unsigned short, struct dns_packet *); + +DNS_PUBLIC unsigned short dns_rr_skip(unsigned short, struct dns_packet *); + +DNS_PUBLIC int dns_rr_cmp(struct dns_rr *, struct dns_packet *, struct dns_rr *, struct dns_packet *); + +DNS_PUBLIC size_t dns_rr_print(void *, size_t, struct dns_rr *, struct dns_packet *, int *); + + +#define dns_rr_i_new(P, ...) \ + dns_rr_i_init(&dns_quietinit((struct dns_rr_i){ 0, __VA_ARGS__ }), (P)) + +struct dns_rr_i { + enum dns_section section; + const void *name; + enum dns_type type; + enum dns_class class; + const void *data; + + int follow; + + int (*sort)(struct dns_rr *, struct dns_rr *, struct dns_rr_i *, struct dns_packet *); + unsigned args[2]; + + struct { + unsigned short next; + unsigned short count; + + unsigned exec; + unsigned regs[2]; + } state, saved; +}; /* struct dns_rr_i */ + +DNS_PUBLIC int dns_rr_i_packet(struct dns_rr *, struct dns_rr *, struct dns_rr_i *, struct dns_packet *); + +DNS_PUBLIC int dns_rr_i_order(struct dns_rr *, struct dns_rr *, struct dns_rr_i *, struct dns_packet *); + +DNS_PUBLIC int dns_rr_i_shuffle(struct dns_rr *, struct dns_rr *, struct dns_rr_i *, struct dns_packet *); + +DNS_PUBLIC struct dns_rr_i *dns_rr_i_init(struct dns_rr_i *, struct dns_packet *); + +#define dns_rr_i_save(i) ((i)->saved = (i)->state) +#define dns_rr_i_rewind(i) ((i)->state = (i)->saved) +#define dns_rr_i_count(i) ((i)->state.count) + +DNS_PUBLIC unsigned dns_rr_grep(struct dns_rr *, unsigned, struct dns_rr_i *, struct dns_packet *, int *); + +#define dns_rr_foreach_(rr, P, ...) \ + for (struct dns_rr_i DNS_PP_XPASTE(i, __LINE__) = *dns_rr_i_new((P), __VA_ARGS__); dns_rr_grep((rr), 1, &DNS_PP_XPASTE(i, __LINE__), (P), &(int){ 0 }); ) + +#define dns_rr_foreach(...) dns_rr_foreach_(__VA_ARGS__) + + +/* + * A R E S O U R C E R E C O R D + */ + +struct dns_a { + struct in_addr addr; +}; /* struct dns_a */ + +DNS_PUBLIC int dns_a_parse(struct dns_a *, struct dns_rr *, struct dns_packet *); + +DNS_PUBLIC int dns_a_push(struct dns_packet *, struct dns_a *); + +DNS_PUBLIC int dns_a_cmp(const struct dns_a *, const struct dns_a *); + +DNS_PUBLIC size_t dns_a_print(void *, size_t, struct dns_a *); + +DNS_PUBLIC size_t dns_a_arpa(void *, size_t, const struct dns_a *); + + +/* + * AAAA R E S O U R C E R E C O R D + */ + +struct dns_aaaa { + struct in6_addr addr; +}; /* struct dns_aaaa */ + +DNS_PUBLIC int dns_aaaa_parse(struct dns_aaaa *, struct dns_rr *, struct dns_packet *); + +DNS_PUBLIC int dns_aaaa_push(struct dns_packet *, struct dns_aaaa *); + +DNS_PUBLIC int dns_aaaa_cmp(const struct dns_aaaa *, const struct dns_aaaa *); + +DNS_PUBLIC size_t dns_aaaa_print(void *, size_t, struct dns_aaaa *); + +DNS_PUBLIC size_t dns_aaaa_arpa(void *, size_t, const struct dns_aaaa *); + + +/* + * MX R E S O U R C E R E C O R D + */ + +struct dns_mx { + unsigned short preference; + char host[DNS_D_MAXNAME + 1]; +}; /* struct dns_mx */ + +DNS_PUBLIC int dns_mx_parse(struct dns_mx *, struct dns_rr *, struct dns_packet *); + +DNS_PUBLIC int dns_mx_push(struct dns_packet *, struct dns_mx *); + +DNS_PUBLIC int dns_mx_cmp(const struct dns_mx *, const struct dns_mx *); + +DNS_PUBLIC size_t dns_mx_print(void *, size_t, struct dns_mx *); + +DNS_PUBLIC size_t dns_mx_cname(void *, size_t, struct dns_mx *); + + +/* + * NS R E S O U R C E R E C O R D + */ + +struct dns_ns { + char host[DNS_D_MAXNAME + 1]; +}; /* struct dns_ns */ + +DNS_PUBLIC int dns_ns_parse(struct dns_ns *, struct dns_rr *, struct dns_packet *); + +DNS_PUBLIC int dns_ns_push(struct dns_packet *, struct dns_ns *); + +DNS_PUBLIC int dns_ns_cmp(const struct dns_ns *, const struct dns_ns *); + +DNS_PUBLIC size_t dns_ns_print(void *, size_t, struct dns_ns *); + +DNS_PUBLIC size_t dns_ns_cname(void *, size_t, struct dns_ns *); + + +/* + * CNAME R E S O U R C E R E C O R D + */ + +struct dns_cname { + char host[DNS_D_MAXNAME + 1]; +}; /* struct dns_cname */ + +DNS_PUBLIC int dns_cname_parse(struct dns_cname *, struct dns_rr *, struct dns_packet *); + +DNS_PUBLIC int dns_cname_push(struct dns_packet *, struct dns_cname *); + +DNS_PUBLIC int dns_cname_cmp(const struct dns_cname *, const struct dns_cname *); + +DNS_PUBLIC size_t dns_cname_print(void *, size_t, struct dns_cname *); + +DNS_PUBLIC size_t dns_cname_cname(void *, size_t, struct dns_cname *); + + +/* + * SOA R E S O U R C E R E C O R D + */ + +struct dns_soa { + char mname[DNS_D_MAXNAME + 1]; + char rname[DNS_D_MAXNAME + 1]; + unsigned serial, refresh, retry, expire, minimum; +}; /* struct dns_soa */ + +DNS_PUBLIC int dns_soa_parse(struct dns_soa *, struct dns_rr *, struct dns_packet *); + +DNS_PUBLIC int dns_soa_push(struct dns_packet *, struct dns_soa *); + +DNS_PUBLIC int dns_soa_cmp(const struct dns_soa *, const struct dns_soa *); + +DNS_PUBLIC size_t dns_soa_print(void *, size_t, struct dns_soa *); + + +/* + * PTR R E S O U R C E R E C O R D + */ + +struct dns_ptr { + char host[DNS_D_MAXNAME + 1]; +}; /* struct dns_ptr */ + +DNS_PUBLIC int dns_ptr_parse(struct dns_ptr *, struct dns_rr *, struct dns_packet *); + +DNS_PUBLIC int dns_ptr_push(struct dns_packet *, struct dns_ptr *); + +DNS_PUBLIC int dns_ptr_cmp(const struct dns_ptr *, const struct dns_ptr *); + +DNS_PUBLIC size_t dns_ptr_print(void *, size_t, struct dns_ptr *); + +DNS_PUBLIC size_t dns_ptr_cname(void *, size_t, struct dns_ptr *); + +DNS_PUBLIC size_t dns_ptr_qname(void *, size_t, int, void *); + + +/* + * SRV R E S O U R C E R E C O R D + */ + +struct dns_srv { + unsigned short priority; + unsigned short weight; + unsigned short port; + char target[DNS_D_MAXNAME + 1]; +}; /* struct dns_srv */ + +DNS_PUBLIC int dns_srv_parse(struct dns_srv *, struct dns_rr *, struct dns_packet *); + +DNS_PUBLIC int dns_srv_push(struct dns_packet *, struct dns_srv *); + +DNS_PUBLIC int dns_srv_cmp(const struct dns_srv *, const struct dns_srv *); + +DNS_PUBLIC size_t dns_srv_print(void *, size_t, struct dns_srv *); + +DNS_PUBLIC size_t dns_srv_cname(void *, size_t, struct dns_srv *); + + +/* + * OPT R E S O U R C E R E C O R D + */ + +#ifndef DNS_OPT_MINDATA +#define DNS_OPT_MINDATA 256 +#endif + +#define DNS_OPT_DNSSEC 0x8000 + +struct dns_opt { + enum dns_rcode rcode; + unsigned char version; + unsigned short flags; + + union { + unsigned short maxsize; /* deprecated as confusing */ + unsigned short maxudp; /* maximum UDP payload size */ + }; + + size_t size, len; + unsigned char data[DNS_OPT_MINDATA]; +}; /* struct dns_opt */ + +#define DNS_OPT_INIT(opt) { .size = sizeof (*opt) - offsetof(struct dns_opt, data) } + +DNS_PUBLIC struct dns_opt *dns_opt_init(struct dns_opt *, size_t); + +DNS_PUBLIC int dns_opt_parse(struct dns_opt *, struct dns_rr *, struct dns_packet *); + +DNS_PUBLIC int dns_opt_push(struct dns_packet *, struct dns_opt *); + +DNS_PUBLIC int dns_opt_cmp(const struct dns_opt *, const struct dns_opt *); + +DNS_PUBLIC size_t dns_opt_print(void *, size_t, struct dns_opt *); + +DNS_PUBLIC unsigned int dns_opt_ttl(const struct dns_opt *); + +DNS_PUBLIC unsigned short dns_opt_class(const struct dns_opt *); + +DNS_PUBLIC dns_error_t dns_opt_data_push(struct dns_opt *, unsigned char, unsigned short, const void *); + + +/* + * SSHFP R E S O U R C E R E C O R D + */ + +struct dns_sshfp { + enum dns_sshfp_key { + DNS_SSHFP_RSA = 1, + DNS_SSHFP_DSA = 2, + } algo; + + enum dns_sshfp_digest { + DNS_SSHFP_SHA1 = 1, + } type; + + union { + unsigned char sha1[20]; + } digest; +}; /* struct dns_sshfp */ + +DNS_PUBLIC int dns_sshfp_parse(struct dns_sshfp *, struct dns_rr *, struct dns_packet *); + +DNS_PUBLIC int dns_sshfp_push(struct dns_packet *, struct dns_sshfp *); + +DNS_PUBLIC int dns_sshfp_cmp(const struct dns_sshfp *, const struct dns_sshfp *); + +DNS_PUBLIC size_t dns_sshfp_print(void *, size_t, struct dns_sshfp *); + + +/* + * TXT R E S O U R C E R E C O R D + */ + +#ifndef DNS_TXT_MINDATA +#define DNS_TXT_MINDATA 1024 +#endif + +struct dns_txt { + size_t size, len; + unsigned char data[DNS_TXT_MINDATA]; +}; /* struct dns_txt */ + +DNS_PUBLIC struct dns_txt *dns_txt_init(struct dns_txt *, size_t); + +DNS_PUBLIC int dns_txt_parse(struct dns_txt *, struct dns_rr *, struct dns_packet *); + +DNS_PUBLIC int dns_txt_push(struct dns_packet *, struct dns_txt *); + +DNS_PUBLIC int dns_txt_cmp(const struct dns_txt *, const struct dns_txt *); + +DNS_PUBLIC size_t dns_txt_print(void *, size_t, struct dns_txt *); + + +/* + * ANY R E S O U R C E R E C O R D + */ + +union dns_any { + struct dns_a a; + struct dns_aaaa aaaa; + struct dns_mx mx; + struct dns_ns ns; + struct dns_cname cname; + struct dns_soa soa; + struct dns_ptr ptr; + struct dns_srv srv; + struct dns_opt opt; + struct dns_sshfp sshfp; + struct dns_txt txt, spf, rdata; +}; /* union dns_any */ + +#define DNS_ANY_INIT(any) { .rdata = { .size = sizeof *(any) - offsetof(struct dns_txt, data) } } + +DNS_PUBLIC union dns_any *dns_any_init(union dns_any *, size_t); + +DNS_PUBLIC int dns_any_parse(union dns_any *, struct dns_rr *, struct dns_packet *); + +DNS_PUBLIC int dns_any_push(struct dns_packet *, union dns_any *, enum dns_type); + +DNS_PUBLIC int dns_any_cmp(const union dns_any *, enum dns_type, const union dns_any *, enum dns_type); + +DNS_PUBLIC size_t dns_any_print(void *, size_t, union dns_any *, enum dns_type); + +DNS_PUBLIC size_t dns_any_cname(void *, size_t, union dns_any *, enum dns_type); + + +/* + * H O S T S I N T E R F A C E + * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +struct dns_hosts; + +DNS_PUBLIC struct dns_hosts *dns_hosts_open(int *); + +DNS_PUBLIC void dns_hosts_close(struct dns_hosts *); + +DNS_PUBLIC dns_refcount_t dns_hosts_acquire(struct dns_hosts *); + +DNS_PUBLIC dns_refcount_t dns_hosts_release(struct dns_hosts *); + +DNS_PUBLIC struct dns_hosts *dns_hosts_mortal(struct dns_hosts *); + +DNS_PUBLIC struct dns_hosts *dns_hosts_local(int *); + +DNS_PUBLIC int dns_hosts_loadfile(struct dns_hosts *, FILE *); + +DNS_PUBLIC int dns_hosts_loadpath(struct dns_hosts *, const char *); + +DNS_PUBLIC int dns_hosts_dump(struct dns_hosts *, FILE *); + +DNS_PUBLIC int dns_hosts_insert(struct dns_hosts *, int, const void *, const void *, _Bool); + +DNS_PUBLIC struct dns_packet *dns_hosts_query(struct dns_hosts *, struct dns_packet *, int *); + + +/* + * R E S O L V . C O N F I N T E R F A C E + * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +struct dns_resolv_conf { + struct sockaddr_storage nameserver[3]; + + char search[4][DNS_D_MAXNAME + 1]; + + /* (f)ile, (b)ind, (c)ache */ + char lookup[4 * (1 + (4 * 2))]; + + /* getaddrinfo family by preference order ("inet4", "inet6") */ + int family[3]; + + struct { + _Bool edns0; + + unsigned ndots; + + unsigned timeout; + + unsigned attempts; + + _Bool rotate; + + _Bool recurse; + + _Bool smart; + + enum { + DNS_RESCONF_TCP_ENABLE, + DNS_RESCONF_TCP_ONLY, + DNS_RESCONF_TCP_DISABLE, + } tcp; + } options; + + struct sockaddr_storage iface; + + struct { /* PRIVATE */ + dns_atomic_t refcount; + } _; +}; /* struct dns_resolv_conf */ + +DNS_PUBLIC struct dns_resolv_conf *dns_resconf_open(int *); + +DNS_PUBLIC void dns_resconf_close(struct dns_resolv_conf *); + +DNS_PUBLIC dns_refcount_t dns_resconf_acquire(struct dns_resolv_conf *); + +DNS_PUBLIC dns_refcount_t dns_resconf_release(struct dns_resolv_conf *); + +DNS_PUBLIC struct dns_resolv_conf *dns_resconf_mortal(struct dns_resolv_conf *); + +DNS_PUBLIC struct dns_resolv_conf *dns_resconf_local(int *); + +DNS_PUBLIC struct dns_resolv_conf *dns_resconf_root(int *); + +DNS_PUBLIC int dns_resconf_pton(struct sockaddr_storage *, const char *); + +DNS_PUBLIC int dns_resconf_loadfile(struct dns_resolv_conf *, FILE *); + +DNS_PUBLIC int dns_resconf_loadpath(struct dns_resolv_conf *, const char *); + +DNS_PUBLIC int dns_nssconf_loadfile(struct dns_resolv_conf *, FILE *); + +DNS_PUBLIC int dns_nssconf_loadpath(struct dns_resolv_conf *, const char *); + +DNS_PUBLIC int dns_resconf_dump(struct dns_resolv_conf *, FILE *); + +DNS_PUBLIC int dns_nssconf_dump(struct dns_resolv_conf *, FILE *); + +DNS_PUBLIC int dns_resconf_setiface(struct dns_resolv_conf *, const char *, unsigned short); + +typedef unsigned long dns_resconf_i_t; + +DNS_PUBLIC size_t dns_resconf_search(void *, size_t, const void *, size_t, struct dns_resolv_conf *, dns_resconf_i_t *); + + +/* + * H I N T S E R V E R I N T E R F A C E + * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +struct dns_hints; + +DNS_PUBLIC struct dns_hints *dns_hints_open(struct dns_resolv_conf *, int *); + +DNS_PUBLIC void dns_hints_close(struct dns_hints *); + +DNS_PUBLIC dns_refcount_t dns_hints_acquire(struct dns_hints *); + +DNS_PUBLIC dns_refcount_t dns_hints_release(struct dns_hints *); + +DNS_PUBLIC struct dns_hints *dns_hints_mortal(struct dns_hints *); + +DNS_PUBLIC int dns_hints_insert(struct dns_hints *, const char *, const struct sockaddr *, unsigned); + +DNS_PUBLIC unsigned dns_hints_insert_resconf(struct dns_hints *, const char *, const struct dns_resolv_conf *, int *); + +DNS_PUBLIC struct dns_hints *dns_hints_local(struct dns_resolv_conf *, int *); + +DNS_PUBLIC struct dns_hints *dns_hints_root(struct dns_resolv_conf *, int *); + +DNS_PUBLIC struct dns_packet *dns_hints_query(struct dns_hints *, struct dns_packet *, int *); + +DNS_PUBLIC int dns_hints_dump(struct dns_hints *, FILE *); + + +struct dns_hints_i { + const char *zone; + + struct { + unsigned next; + unsigned seed; + } state; +}; /* struct dns_hints_i */ + +#define dns_hints_i_new(...) (&(struct dns_hints_i){ __VA_ARGS__ }) + +DNS_PUBLIC unsigned dns_hints_grep(struct sockaddr **, socklen_t *, unsigned, struct dns_hints_i *, struct dns_hints *); + + +/* + * C A C H E I N T E R F A C E + * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +struct dns_cache { + void *state; + + dns_refcount_t (*acquire)(struct dns_cache *); + dns_refcount_t (*release)(struct dns_cache *); + + struct dns_packet *(*query)(struct dns_packet *, struct dns_cache *, int *); + + int (*submit)(struct dns_packet *, struct dns_cache *); + int (*check)(struct dns_cache *); + struct dns_packet *(*fetch)(struct dns_cache *, int *); + + int (*pollfd)(struct dns_cache *); + short (*events)(struct dns_cache *); + void (*clear)(struct dns_cache *); + + union { + long i; + void *p; + } arg[3]; + + struct { /* PRIVATE */ + dns_atomic_t refcount; + } _; +}; /* struct dns_cache */ + + +DNS_PUBLIC struct dns_cache *dns_cache_init(struct dns_cache *); + +DNS_PUBLIC void dns_cache_close(struct dns_cache *); + + +/* + * A P P L I C A T I O N I N T E R F A C E + * + * Options to change the behavior of the API. Applies across all the + * different components. + * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#define DNS_OPTS_INITIALIZER_ { 0, 0 }, 0 +#define DNS_OPTS_INITIALIZER { DNS_OPTS_INITIALIZER_ } +#define DNS_OPTS_INIT(...) { DNS_OPTS_INITIALIZER_, __VA_ARGS__ } + +#define dns_opts(...) (&dns_quietinit((struct dns_options)DNS_OPTS_INIT(__VA_ARGS__))) + +struct dns_options { + /* + * If the callback closes *fd, it must set it to -1. Otherwise, the + * descriptor is queued and lazily closed at object destruction or + * by an explicit call to _clear(). This allows safe use of + * kqueue(2), epoll(2), et al -style persistent events. + */ + struct { + void *arg; + int (*cb)(int *fd, void *arg); + } closefd; + + /* bitmask for _events() routines */ + enum dns_events { + DNS_SYSPOLL, + DNS_LIBEVENT, + } events; +}; /* struct dns_options */ + + +/* + * S T A T S I N T E R F A C E S + * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +struct dns_stat { + size_t queries; + + struct { + struct { + size_t count, bytes; + } sent, rcvd; + } udp, tcp; +}; /* struct dns_stat */ + + +/* + * S O C K E T I N T E R F A C E + * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +struct dns_socket; + +DNS_PUBLIC struct dns_socket *dns_so_open(const struct sockaddr *, int, const struct dns_options *, int *error); + +DNS_PUBLIC void dns_so_close(struct dns_socket *); + +DNS_PUBLIC void dns_so_reset(struct dns_socket *); + +DNS_PUBLIC unsigned short dns_so_mkqid(struct dns_socket *so); + +DNS_PUBLIC struct dns_packet *dns_so_query(struct dns_socket *, struct dns_packet *, struct sockaddr *, int *); + +DNS_PUBLIC int dns_so_submit(struct dns_socket *, struct dns_packet *, struct sockaddr *); + +DNS_PUBLIC int dns_so_check(struct dns_socket *); + +DNS_PUBLIC struct dns_packet *dns_so_fetch(struct dns_socket *, int *); + +DNS_PUBLIC time_t dns_so_elapsed(struct dns_socket *); + +DNS_PUBLIC void dns_so_clear(struct dns_socket *); + +DNS_PUBLIC int dns_so_events(struct dns_socket *); + +DNS_PUBLIC int dns_so_pollfd(struct dns_socket *); + +DNS_PUBLIC int dns_so_poll(struct dns_socket *, int); + +DNS_PUBLIC const struct dns_stat *dns_so_stat(struct dns_socket *); + + +/* + * R E S O L V E R I N T E R F A C E + * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +struct dns_resolver; + +DNS_PUBLIC struct dns_resolver *dns_res_open(struct dns_resolv_conf *, struct dns_hosts *hosts, struct dns_hints *, struct dns_cache *, const struct dns_options *, int *); + +DNS_PUBLIC struct dns_resolver *dns_res_stub(const struct dns_options *, int *); + +DNS_PUBLIC void dns_res_reset(struct dns_resolver *); + +DNS_PUBLIC void dns_res_close(struct dns_resolver *); + +DNS_PUBLIC dns_refcount_t dns_res_acquire(struct dns_resolver *); + +DNS_PUBLIC dns_refcount_t dns_res_release(struct dns_resolver *); + +DNS_PUBLIC struct dns_resolver *dns_res_mortal(struct dns_resolver *); + +DNS_PUBLIC int dns_res_submit(struct dns_resolver *, const char *, enum dns_type, enum dns_class); + +DNS_PUBLIC int dns_res_submit2(struct dns_resolver *, const char *, size_t, enum dns_type, enum dns_class); + +DNS_PUBLIC int dns_res_check(struct dns_resolver *); + +DNS_PUBLIC struct dns_packet *dns_res_fetch(struct dns_resolver *, int *); + +DNS_PUBLIC time_t dns_res_elapsed(struct dns_resolver *); + +DNS_PUBLIC void dns_res_clear(struct dns_resolver *); + +DNS_PUBLIC int dns_res_events(struct dns_resolver *); + +DNS_PUBLIC int dns_res_pollfd(struct dns_resolver *); + +DNS_PUBLIC time_t dns_res_timeout(struct dns_resolver *); + +DNS_PUBLIC int dns_res_poll(struct dns_resolver *, int); + +DNS_PUBLIC struct dns_packet *dns_res_query(struct dns_resolver *, const char *, enum dns_type, enum dns_class, int, int *); + +DNS_PUBLIC const struct dns_stat *dns_res_stat(struct dns_resolver *); + +DNS_PUBLIC void dns_res_sethints(struct dns_resolver *, struct dns_hints *); + + +/* + * A D D R I N F O I N T E R F A C E + * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +struct dns_addrinfo; + +DNS_PUBLIC struct dns_addrinfo *dns_ai_open(const char *, const char *, enum dns_type, const struct addrinfo *, struct dns_resolver *, int *); + +DNS_PUBLIC void dns_ai_close(struct dns_addrinfo *); + +DNS_PUBLIC int dns_ai_nextent(struct addrinfo **, struct dns_addrinfo *); + +DNS_PUBLIC size_t dns_ai_print(void *, size_t, struct addrinfo *, struct dns_addrinfo *); + +DNS_PUBLIC time_t dns_ai_elapsed(struct dns_addrinfo *); + +DNS_PUBLIC void dns_ai_clear(struct dns_addrinfo *); + +DNS_PUBLIC int dns_ai_events(struct dns_addrinfo *); + +DNS_PUBLIC int dns_ai_pollfd(struct dns_addrinfo *); + +DNS_PUBLIC time_t dns_ai_timeout(struct dns_addrinfo *); + +DNS_PUBLIC int dns_ai_poll(struct dns_addrinfo *, int); + +DNS_PUBLIC const struct dns_stat *dns_ai_stat(struct dns_addrinfo *); + + +/* + * U T I L I T Y I N T E R F A C E S + * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +DNS_PUBLIC size_t dns_strlcpy(char *, const char *, size_t); + +DNS_PUBLIC size_t dns_strlcat(char *, const char *, size_t); + + +/* + * M A C R O M A G I C S + * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#define DNS_PP_MIN(a, b) (((a) < (b))? (a) : (b)) +#define DNS_PP_MAX(a, b) (((a) > (b))? (a) : (b)) +#define DNS_PP_NARG_(a, b, c, d, e, f, g, h, i, j, k, N,...) N +#define DNS_PP_NARG(...) DNS_PP_NARG_(__VA_ARGS__, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0) +#define DNS_PP_CALL(F, ...) F(__VA_ARGS__) +#define DNS_PP_PASTE(x, y) x##y +#define DNS_PP_XPASTE(x, y) DNS_PP_PASTE(x, y) +#define DNS_PP_STRINGIFY_(s) #s +#define DNS_PP_STRINGIFY(s) DNS_PP_STRINGIFY_(s) +#define DNS_PP_D1 0 +#define DNS_PP_D2 1 +#define DNS_PP_D3 2 +#define DNS_PP_D4 3 +#define DNS_PP_D5 4 +#define DNS_PP_D6 5 +#define DNS_PP_D7 6 +#define DNS_PP_D8 7 +#define DNS_PP_D9 8 +#define DNS_PP_D10 9 +#define DNS_PP_D11 10 +#define DNS_PP_DEC(N) DNS_PP_XPASTE(DNS_PP_D, N) + +#endif /* DNS_H */ diff --git a/include/oonf/generic/dns_query/dns_query.h b/include/oonf/generic/dns_query/dns_query.h new file mode 100644 index 00000000..822bb476 --- /dev/null +++ b/include/oonf/generic/dns_query/dns_query.h @@ -0,0 +1,103 @@ +/** + * Copyright (C) 2019 Fraunhofer FKIE + * + * @author Henning Rogge + */ + +/** + * @file + */ + +#ifndef OONF_DNS_QUERY_H_ +#define OONF_DNS_QUERY_H_ + +#include +#include +#include + +#include +#include + +#include + +#define OONF_DNS_QUERY_SUBSYSTEM "dns_query" + +enum { + OONF_DNS_HOSTNAME_LENGTH = 256 +}; + +enum dns_service_query_status { + DNS_SERVICE_QUERY_SUCCESSFUL, + DNS_SERVICE_QUERY_NETWORK_ERROR, + DNS_SERVICE_QUERY_NO_HOSTNAME +}; + +struct oonf_dns_query { + /*! DNS server to query */ + union netaddr_socket *dns_server; + + /*! DNS client socket */ + union netaddr_socket *dns_client; + + enum dns_type dns_type; + + const char *query; + + const char *socket_name; + + /* function to call when done */ + void (*cb_done)(struct oonf_dns_query *, bool timeout); + + /* function to call with result */ + struct { + void (*srv_result)(struct oonf_dns_query *, struct dns_srv *response); + void (*a_result)(struct oonf_dns_query *, struct dns_a *response); + void (*aaaa_result)(struct oonf_dns_query *, struct dns_aaaa *response); + void (*ptr_result)(struct oonf_dns_query *, struct dns_ptr *response); + void (*any_result)(struct oonf_dns_query *, enum dns_type type, union dns_any *response); + } cb; + + uint64_t timeout; + + union { + struct dns_packet packet; + uint8_t full_length[dns_p_calcsize((512))]; + } _bin_query; + + struct dns_socket *_dns_socket; + struct oonf_socket_entry _socket_entry; + struct oonf_timer_instance _timeout; +}; + +int dns_query_do(struct oonf_dns_query *q); + +static INLINE int +dns_query_srv(struct oonf_dns_query *q) { + q->dns_type = DNS_T_SRV; + return dns_query_do(q); +} + +static INLINE int +dns_query_a(struct oonf_dns_query *q) { + q->dns_type = DNS_T_A; + return dns_query_do(q); +} + +static INLINE int +dns_query_aaaa(struct oonf_dns_query *q) { + q->dns_type = DNS_T_AAAA; + return dns_query_do(q); +} + +static INLINE int +dns_query_ptr(struct oonf_dns_query *q) { + q->dns_type = DNS_T_PTR; + return dns_query_do(q); +} + +static INLINE enum dns_type +dns_query_get_type(struct oonf_dns_query *q) { + return q->dns_type; +} + +#endif /* IF_NO_MULTICAST_DNS_QUERY_H_ */ diff --git a/include/oonf/generic/dns_sd/dns_sd.h b/include/oonf/generic/dns_sd/dns_sd.h new file mode 100644 index 00000000..8a9804a4 --- /dev/null +++ b/include/oonf/generic/dns_sd/dns_sd.h @@ -0,0 +1,126 @@ +/** + * Copyright (C) 2019 Fraunhofer FKIE + * + * @author Henning Rogge + */ + +/** + * @file + */ + +#ifndef OONF_DNS_SD_H_ +#define OONF_DNS_SD_H_ + +#include +#include + +#define OONF_DNS_SD_SUBSYSTEM "dns_sd" + +enum { + DNS_SD_PREFIX_LENGTH = 64, +}; + +struct dns_sd_prefix { + char dns_prefix[DNS_SD_PREFIX_LENGTH]; + + uint64_t _flag; + uint32_t _usage; + + struct avl_node _node; +}; + +struct dns_sd_context_key { + /*! name of interface this IP belongs to */ + char interface[IF_NAMESIZE]; + + /*! IP address that was queried about services */ + struct netaddr ip; +}; + +struct dns_sd_context { + /*! primary key for DNS-SD context */ + struct dns_sd_context_key key; + + /*! hostname of the context IP */ + char *hostname; + + /*! cache which includes all sd_prefixes that are available for this l2 neighbor */ + uint64_t available; + + /*! cache which includes all sd_prefixes that are unavailable for this l2 neighbor */ + uint64_t unavailable; + + /*! tree of all prefix results for this context */ + struct avl_tree _service_tree; + + /*! list of neighbors in the working queue */ + struct list_entity _working_node; + + /*! tree node for global list of DNS-SD results */ + struct avl_node _global_node; +}; + +struct dns_sd_service_key { + /*! hostname of the target with the service */ + const char *hostname; + + /*! prefix this service result belongs to */ + struct dns_sd_prefix *prefix; +}; + +struct dns_sd_service { + /*! unique key for service in the context */ + struct dns_sd_service_key key; + + /*! port number of service */ + uint16_t port; + + /*! weight factor of service */ + uint16_t weight; + + /*! priority factor of service */ + uint16_t priority; + + /*! IPv4 address to connect to service */ + struct netaddr ipv4; + + /*! IPv6 address to connect to service */ + struct netaddr ipv6; + + /*! node of DNS-SD context tree */ + struct avl_node _node; +}; + +enum dns_sd_prefix_status { + DNS_SD_PREFIX_STATE_UNKNOWN, + DNS_SD_PREFIX_AVAILABLE, + DNS_SD_PREFIX_UNAVAILABLE, +}; + +EXPORT struct dns_sd_prefix *dns_sd_add(const char *prefix); +EXPORT void dns_sd_remove(struct dns_sd_prefix *); +EXPORT struct avl_tree *dns_sd_get_prefix_tree(void); +EXPORT struct avl_tree *dns_sd_get_context_tree(void); +EXPORT struct dns_sd_context *dns_sd_context_get( + const char *interface, const struct netaddr *ip); + +static INLINE struct dns_sd_service * +dns_sd_service_get(struct dns_sd_context *context, const char *hostname, struct dns_sd_prefix *prefix) { + struct dns_sd_service *service; + struct dns_sd_service_key key = { .hostname = hostname, .prefix = prefix }; + + return avl_find_element(&context->_service_tree, &key, service, _node); +} + +static INLINE enum dns_sd_prefix_status +dns_sd_context_has_prefix(struct dns_sd_prefix *prefix, struct dns_sd_context *context) { + if ((context->available & prefix->_flag) != 0) { + return DNS_SD_PREFIX_AVAILABLE; + } + if ((context->unavailable & prefix->_flag) != 0) { + return DNS_SD_PREFIX_UNAVAILABLE; + } + return DNS_SD_PREFIX_STATE_UNKNOWN; +} + +#endif diff --git a/include/oonf/generic/dns_sd/dns_sd_intern.h b/include/oonf/generic/dns_sd/dns_sd_intern.h new file mode 100644 index 00000000..94b10260 --- /dev/null +++ b/include/oonf/generic/dns_sd/dns_sd_intern.h @@ -0,0 +1,38 @@ +/** + * Copyright (C) 2019 Fraunhofer FKIE + * + * @author Henning Rogge + */ + +/** + * @file + */ + +#ifndef OONF_DNS_SD_INTERN_H_ +#define OONF_DNS_SD_INTERN_H_ + +#include +#include +#include + +#include + +struct dns_sd_query { + struct oonf_dns_query dns; + + union netaddr_socket server; + union netaddr_socket client; + + struct dns_sd_context *context; + + char name[512]; + struct dns_sd_prefix *prefix; + + struct { + struct dns_sd_service *service; + struct netaddr ipv4; + struct netaddr ipv6; + } srv_result; +}; + +#endif /* OONF_DNS_SD_INTERN_H_ */ diff --git a/include/oonf/olsrv2/olsrv2/olsrv2_tc.h b/include/oonf/olsrv2/olsrv2/olsrv2_tc.h index 7333458d..70835ff0 100644 --- a/include/oonf/olsrv2/olsrv2/olsrv2_tc.h +++ b/include/oonf/olsrv2/olsrv2/olsrv2_tc.h @@ -251,11 +251,11 @@ olsrv2_tc_is_node_virtual(struct olsrv2_tc_node *node) { } /** - * @param prefix network prefix of tc endpoint + * @param prefix network prefix of tc endpoint, might be source specific * @return pointer to tc endpoint, NULL if not found */ static INLINE struct olsrv2_tc_endpoint * -olsrv2_tc_endpoint_get(struct netaddr *prefix) { +olsrv2_tc_endpoint_get(struct os_route_key *prefix) { struct olsrv2_tc_endpoint *end; return avl_find_element(olsrv2_tc_get_endpoint_tree(), prefix, end, _node); diff --git a/src/base/oonf_packet_socket.c b/src/base/oonf_packet_socket.c index 3b05b198..64c21813 100644 --- a/src/base/oonf_packet_socket.c +++ b/src/base/oonf_packet_socket.c @@ -676,9 +676,10 @@ _apply_managed_socket(struct oonf_packet_managed *managed, struct oonf_packet_so } if (list_is_node_added(&packet->node)) { - if (data == packet->os_if && memcmp(&sock, &packet->local_socket, sizeof(sock)) == 0 && - protocol == packet->protocol) { - /* nothing changed */ + if (data == packet->os_if && memcmp(&sock, &packet->local_socket, sizeof(sock)) == 0 + && protocol == packet->protocol) { + /* nothing changed, just copy config again */ + memcpy(&packet->config, &managed->config, sizeof(packet->config)); return 1; } } diff --git a/src/generic/CMakeLists.txt b/src/generic/CMakeLists.txt index e82e8fc9..99edc348 100644 --- a/src/generic/CMakeLists.txt +++ b/src/generic/CMakeLists.txt @@ -1,6 +1,8 @@ # add subdirectories add_subdirectory(cfg_compact) add_subdirectory(dlep) +add_subdirectory(dns_query) +add_subdirectory(dns_sd) add_subdirectory(example) add_subdirectory(layer2info) add_subdirectory(layer2_import) diff --git a/src/generic/dlep/CMakeLists.txt b/src/generic/dlep/CMakeLists.txt index 9a474efe..33fde4a3 100644 --- a/src/generic/dlep/CMakeLists.txt +++ b/src/generic/dlep/CMakeLists.txt @@ -14,6 +14,7 @@ SET (common_source ext_base_metric/metric.c ext_l2_statistics/l2_statistics.c ext_radio_attributes/radio_attributes.c ext_lid/lid.c + ext_dns/dns.c dlep_extension.c dlep_interface.c dlep_session.c @@ -28,7 +29,7 @@ SET (radio_include radio/dlep_radio.h) # sources/includes for dlep_router SET (router_source ${common_source} ${router_only_source}) -SET (router_include router/dlep_router.h) +SET (router_include router/dlep_router.h) # sources/includes for dlep_proxy SET (proxy_source ${common_source} diff --git a/src/generic/dlep/dlep_reader.c b/src/generic/dlep/dlep_reader.c index fb152acc..7752ce5c 100644 --- a/src/generic/dlep/dlep_reader.c +++ b/src/generic/dlep/dlep_reader.c @@ -385,6 +385,88 @@ dlep_reader_ipv6_conpoint_tlv( return netaddr_from_binary(addr, &ptr[1], 16, AF_INET6); } +/** + * Parse a DLEP IPv4 DNS TLV + * @param addr pointer to address storage + * @param port pointer to port storage + * @param tls pointer to storage for TLV flag + * @param session dlep session + * @param value dlep value to parse, NULL for using the first + * DLEP_IPv4_DNS_SERVER_TLV value + * @return -1 if an error happened, 0 otherwise + */ +int +dlep_reader_ipv4_dns_tlv( + union netaddr_socket *sock, struct dlep_session *session, struct dlep_parser_value *value) { + struct netaddr addr; + uint16_t tmp; + const uint8_t *ptr; + + if (!value) { + value = dlep_session_get_tlv_value(session, DLEP_IPV4_DNS_SERVER_TLV); + if (!value) { + return -1; + } + } + + if (value->length != 6) { + return -1; + } + + ptr = dlep_session_get_tlv_binary(session, value); + + /* handle port */ + memcpy(&tmp, &ptr[4], sizeof(tmp)); + tmp = ntohs(tmp); + + if (netaddr_from_binary(&addr, &ptr[0], 4, AF_INET)) { + return -1; + } + + return netaddr_socket_init(sock, &addr, tmp, session->l2_listener.data->_addr_initialized); +} + +/** + * Parse a DLEP IPv6 DNS TLV + * @param addr pointer to address storage + * @param port pointer to port storage + * @param tls pointer to storage for TLV flag + * @param session dlep session + * @param value dlep value to parse, NULL for using the first + * DLEP_IPv6_DNS_SERVER_TLV value + * @return -1 if an error happened, 0 otherwise + */ +int +dlep_reader_ipv6_dns_tlv( + union netaddr_socket *sock, struct dlep_session *session, struct dlep_parser_value *value) { + struct netaddr addr; + uint16_t tmp; + const uint8_t *ptr; + + if (!value) { + value = dlep_session_get_tlv_value(session, DLEP_IPV6_DNS_SERVER_TLV); + if (!value) { + return -1; + } + } + + if (value->length != 18) { + return -1; + } + + ptr = dlep_session_get_tlv_binary(session, value); + + /* handle port */ + memcpy(&tmp, &ptr[16], sizeof(tmp)); + tmp = ntohs(tmp); + + if (netaddr_from_binary(&addr, &ptr[0], 4, AF_INET)) { + return -1; + } + + return netaddr_socket_init(sock, &addr, tmp, session->l2_listener.data->_addr_initialized); +} + /** * Parse a generic uint64 value TLV * @param number storage for uint64 value @@ -500,11 +582,17 @@ dlep_reader_map_identity(struct oonf_layer2_data *data, const struct oonf_layer2 uint16_t tmp16; uint8_t tmp8; const uint8_t *dlepvalue; + struct netaddr addr; + union netaddr_socket sock; value = dlep_session_get_tlv_value(session, dlep_tlv); - if (value) { - dlepvalue = dlep_parser_get_tlv_binary(&session->parser, value); + if (!value) { + return 0; + } + dlepvalue = dlep_parser_get_tlv_binary(&session->parser, value); + + if (meta->type == OONF_LAYER2_INTEGER_DATA || meta->type == OONF_LAYER2_BOOLEAN_DATA) { switch (value->length) { case 8: memcpy(&tmp64, dlepvalue, 8); @@ -525,17 +613,31 @@ dlep_reader_map_identity(struct oonf_layer2_data *data, const struct oonf_layer2 default: return -1; } + } - switch (meta->type) { - case OONF_LAYER2_INTEGER_DATA: - oonf_layer2_data_set_int64(data, session->l2_origin, meta, l2value, scaling); - break; - case OONF_LAYER2_BOOLEAN_DATA: - oonf_layer2_data_set_bool(data, session->l2_origin, meta, l2value != 0); - break; - default: - return -1; - } + switch (meta->type) { + case OONF_LAYER2_INTEGER_DATA: + oonf_layer2_data_set_int64(data, session->l2_origin, meta, l2value, scaling); + break; + case OONF_LAYER2_BOOLEAN_DATA: + oonf_layer2_data_set_bool(data, session->l2_origin, meta, l2value != 0); + break; + case OONF_LAYER2_NETWORK_DATA: + netaddr_from_binary(&addr, dlepvalue, value->length, 0); + oonf_layer2_data_set_netaddr(data, session->l2_origin, meta, &addr); + break; + case OONF_LAYER2_SOCKET_DATA: + netaddr_socket_invalidate(&sock); + if (value->length == 6 || value->length == 18) { + netaddr_from_binary(&addr, dlepvalue, value->length-2, 0); + memcpy(&tmp16, &dlepvalue[value->length-2], 2); + + netaddr_socket_init(&sock, &addr, ntohs(tmp16), session->l2_listener.data->index); + } + oonf_layer2_data_set_socket(data, session->l2_origin, meta, &sock); + break; + default: + return -1; } return 0; } @@ -554,12 +656,18 @@ int dlep_reader_map_l2neigh_data(struct oonf_layer2_data *data, struct dlep_session *session, struct dlep_extension *ext) { struct dlep_neighbor_mapping *map; + enum oonf_layer2_neighbor_index l2neigh_idx; + size_t i; for (i = 0; i < ext->neigh_mapping_count; i++) { map = &ext->neigh_mapping[i]; - if (map->from_tlv(&data[map->layer2], oonf_layer2_neigh_metadata_get(map->layer2), session, + l2neigh_idx = map->layer2_dst; + if (l2neigh_idx == 0) { + l2neigh_idx = map->layer2; + } + if (map->from_tlv(&data[l2neigh_idx], oonf_layer2_neigh_metadata_get(l2neigh_idx), session, map->dlep, map->scaling)) { return -(i + 1); } @@ -580,12 +688,17 @@ dlep_reader_map_l2neigh_data(struct oonf_layer2_data *data, struct dlep_session int dlep_reader_map_l2net_data(struct oonf_layer2_data *data, struct dlep_session *session, struct dlep_extension *ext) { struct dlep_network_mapping *map; + enum oonf_layer2_network_index l2net_idx; size_t i; for (i = 0; i < ext->if_mapping_count; i++) { map = &ext->if_mapping[i]; - if (map->from_tlv(&data[map->layer2], oonf_layer2_net_metadata_get(map->layer2), session, + l2net_idx = map->layer2_dst; + if (l2net_idx == 0) { + l2net_idx = map->layer2; + } + if (map->from_tlv(&data[l2net_idx], oonf_layer2_net_metadata_get(l2net_idx), session, map->dlep, map->scaling)) { return -(i + 1); } diff --git a/src/generic/dlep/dlep_writer.c b/src/generic/dlep/dlep_writer.c index 1b93e16b..5cc145ad 100644 --- a/src/generic/dlep/dlep_writer.c +++ b/src/generic/dlep/dlep_writer.c @@ -312,6 +312,64 @@ dlep_writer_add_ipv6_conpoint_tlv(struct dlep_writer *writer, const struct netad dlep_writer_add_tlv(writer, DLEP_IPV6_CONPOINT_TLV, &value, sizeof(value)); } +/** + * Write a DLEP IPv4 conpoint TLV + * @param writer dlep writer + * @param addr IPv4 address + * @param port port number + */ +void +dlep_writer_add_ipv4_dns_tlv(struct dlep_writer *writer, const union netaddr_socket *sock) { + struct netaddr ip; + uint16_t port; + uint8_t value[6]; + + netaddr_from_socket(&ip, sock); + port = netaddr_socket_get_port(sock); + + if (netaddr_get_address_family(&ip) != AF_INET) { + return; + } + + /* convert port to network byte order */ + port = htons(port); + + /* copy data into value buffer */ + netaddr_to_binary(&value[0], &ip, sizeof(value)); + memcpy(&value[4], &port, sizeof(port)); + + dlep_writer_add_tlv(writer, DLEP_IPV4_DNS_SERVER_TLV, &value, sizeof(value)); +} + +/** + * Write a DLEP IPv6 conpoint TLV + * @param writer dlep writer + * @param addr IPv6 address + * @param port port number + */ +void +dlep_writer_add_ipv6_dns_tlv(struct dlep_writer *writer, const union netaddr_socket *sock) { + struct netaddr ip; + uint16_t port; + uint8_t value[18]; + + netaddr_from_socket(&ip, sock); + port = netaddr_socket_get_port(sock); + + if (netaddr_get_address_family(&ip) != AF_INET6) { + return; + } + + /* convert port to network byte order */ + port = htons(port); + + /* copy data into value buffer */ + netaddr_to_binary(&value[0], &ip, sizeof(value)); + memcpy(&value[16], &port, sizeof(port)); + + dlep_writer_add_tlv(writer, DLEP_IPV6_DNS_SERVER_TLV, &value, sizeof(value)); +} + /** * Add a DLEP tlv with uint64 value * @param writer dlep writer @@ -376,15 +434,15 @@ dlep_writer_add_supported_extensions(struct dlep_writer *writer, const uint16_t } /** - * Write a layer2 data object into a DLEP TLV + * Write a layer2 number object into a DLEP TLV * @param writer dlep writer * @param data layer2 data * @param tlv tlv id * @param length tlv value length (1,2,4 or 8 bytes) * @return -1 if an error happened, 0 otherwise */ -int -dlep_writer_map_identity(struct dlep_writer *writer, struct oonf_layer2_data *data, +static int +_map_number(struct dlep_writer *writer, struct oonf_layer2_data *data, const struct oonf_layer2_metadata *meta, uint16_t tlv, uint16_t length, uint64_t scaling) { int64_t l2value64; uint64_t tmp64; @@ -393,24 +451,11 @@ dlep_writer_map_identity(struct dlep_writer *writer, struct oonf_layer2_data *da uint8_t tmp8; void *value; - if (!oonf_layer2_data_has_value(data)) { - /* no data available */ - return 0; + if (meta->type == OONF_LAYER2_INTEGER_DATA) { + l2value64 = oonf_layer2_data_get_int64(data, scaling, 0); } - if (meta->type != oonf_layer2_data_get_type(data)) { - /* bad data type */ - return -1; - } - - switch (oonf_layer2_data_get_type(data)) { - case OONF_LAYER2_INTEGER_DATA: - l2value64 = oonf_layer2_data_get_int64(data, scaling, 0); - break; - case OONF_LAYER2_BOOLEAN_DATA: - l2value64 = oonf_layer2_data_get_boolean(data, false) ? 1 : 0; - break; - default: - return -1; + else { + l2value64 = oonf_layer2_data_get_boolean(data, false) ? 1 : 0; } switch (length) { @@ -438,6 +483,86 @@ dlep_writer_map_identity(struct dlep_writer *writer, struct oonf_layer2_data *da return 0; } +/** + * Write a layer2 network object into a DLEP TLV + * @param writer dlep writer + * @param data layer2 data + * @param tlv tlv id + * @param length tlv value length (4, 6, 16 or 18 bytes) + * @return -1 if an error happened, 0 otherwise + */ +static int +_map_network(struct dlep_writer *writer, struct oonf_layer2_data *data, + const struct oonf_layer2_metadata *meta, uint16_t tlv, uint16_t length) { + const union netaddr_socket *sock; + const struct netaddr *addr; + const uint8_t *addr_bin; + uint8_t buffer[18]; + uint16_t port; + size_t addr_len; + + /* extract address (and port for sockets) */ + if (meta->type == OONF_LAYER2_NETWORK_DATA) { + addr = oonf_layer2_data_get_netaddr(data); + addr_bin = netaddr_get_binptr(addr); + addr_len = netaddr_get_binlength(addr); + } + else { + sock = oonf_layer2_data_get_socket(data); + addr_bin = netaddr_socket_get_addr_binptr(sock); + addr_len = netaddr_socket_get_addr_binlength(sock); + port = netaddr_socket_get_port(sock); + } + + if (addr_len == 0) { + /* unspecified address, don't produce TLV */ + return 0; + } + + /* check address length */ + if (addr_len + (meta->type == OONF_LAYER2_SOCKET_DATA ? 2 : 0) != length) { + return -1; + } + + memcpy(buffer, addr_bin, addr_len); + if (meta->type == OONF_LAYER2_SOCKET_DATA) { + port = htons(port); + memcpy(&buffer[addr_len], &port, sizeof(port)); + } + + dlep_writer_add_tlv(writer, tlv, buffer, length); + return 0; +} + +/** + * Write a layer2 data object into a DLEP TLV + * @param writer dlep writer + * @param data layer2 data + * @param tlv tlv id + * @param length tlv value length (1,2,4 or 8 bytes) + * @return -1 if an error happened, 0 otherwise + */ +int +dlep_writer_map_identity(struct dlep_writer *writer, struct oonf_layer2_data *data, + const struct oonf_layer2_metadata *meta, uint16_t tlv, uint16_t length, uint64_t scaling) { + if (!oonf_layer2_data_has_value(data)) { + /* no data available */ + return 0; + } + if (meta->type != oonf_layer2_data_get_type(data)) { + /* bad data type */ + return -1; + } + + if (meta->type == OONF_LAYER2_INTEGER_DATA || meta->type == OONF_LAYER2_BOOLEAN_DATA) { + return _map_number(writer, data, meta, tlv, length, scaling); + } + if (meta->type == OONF_LAYER2_NETWORK_DATA || meta->type == OONF_LAYER2_SOCKET_DATA) { + return _map_network(writer, data, meta, tlv, length); + } + return -1; +} + /** * Automatically map all predefined metric values of an * extension for layer2 neighbor data from the layer2 @@ -464,7 +589,8 @@ dlep_writer_map_l2neigh_data( ptr = &def[map->layer2]; } - if (map->to_tlv(writer, ptr, oonf_layer2_neigh_metadata_get(map->layer2), map->dlep, map->length, map->scaling)) { + if (map->to_tlv(writer, ptr, oonf_layer2_neigh_metadata_get(map->layer2), + map->dlep, map->length, map->scaling)) { return -(i + 1); } } @@ -483,13 +609,19 @@ dlep_writer_map_l2neigh_data( */ int dlep_writer_map_l2net_data(struct dlep_writer *writer, struct dlep_extension *ext, struct oonf_layer2_data *data) { + enum oonf_layer2_network_index l2net_idx; struct dlep_network_mapping *map; size_t i; for (i = 0; i < ext->if_mapping_count; i++) { map = &ext->if_mapping[i]; - if (map->to_tlv(writer, &data[map->layer2], oonf_layer2_net_metadata_get(map->layer2), map->dlep, map->length, map->scaling)) { + l2net_idx = map->layer2_dst; + if (l2net_idx == 0) { + l2net_idx = map->layer2; + } + if (map->to_tlv(writer, &data[map->layer2], oonf_layer2_net_metadata_get(l2net_idx), + map->dlep, map->length, map->scaling)) { return -(i + 1); } } diff --git a/src/generic/dlep/ext_dns/dns.c b/src/generic/dlep/ext_dns/dns.c new file mode 100644 index 00000000..eb450a6c --- /dev/null +++ b/src/generic/dlep/ext_dns/dns.c @@ -0,0 +1,137 @@ + +/* + * The olsr.org Optimized Link-State Routing daemon version 2 (olsrd2) + * Copyright (c) 2004-2015, the olsr.org team - see HISTORY file + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of olsr.org, olsrd nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * Visit http://www.olsr.org for more information. + * + * If you find this software useful feel free to make a donation + * to the project. For more information see the website or contact + * the copyright holders. + * + */ + +/** + * @file + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +/* peer initialization ack/peer update/destination update */ +static const uint16_t _dns_tlvs[] = { + DLEP_IPV4_DNS_SERVER_TLV, + DLEP_IPV6_DNS_SERVER_TLV, +}; + +/* supported signals of this extension */ +static struct dlep_extension_signal _signals[] = { + { + .id = DLEP_SESSION_INITIALIZATION_ACK, + .supported_tlvs = _dns_tlvs, + .supported_tlv_count = ARRAYSIZE(_dns_tlvs), + .add_radio_tlvs = dlep_extension_radio_write_session_init_ack, + .process_router = dlep_extension_router_process_session_init_ack, + }, +}; + +/* supported TLVs of this extension */ +static struct dlep_extension_tlv _tlvs[] = { + { DLEP_IPV4_DNS_SERVER_TLV, 6, 6 }, + { DLEP_IPV6_DNS_SERVER_TLV, 18, 18 }, +}; + +static struct dlep_network_mapping _net_mappings[] = { + { + .dlep = DLEP_IPV4_DNS_SERVER_TLV, + .layer2 = OONF_LAYER2_NET_IPV4_LOCAL_DNS, + .layer2_dst = OONF_LAYER2_NET_IPV4_REMOTE_DNS, + .length = 6, + + .mandatory = true, + .default_value.socket = NETADDR_SOCKET_UNSPEC_INIT, + + .from_tlv = dlep_reader_map_identity, + .to_tlv = dlep_writer_map_identity, + }, + { + .dlep = DLEP_IPV6_DNS_SERVER_TLV, + .layer2 = OONF_LAYER2_NET_IPV6_LOCAL_DNS, + .layer2_dst = OONF_LAYER2_NET_IPV6_REMOTE_DNS, + .length = 18, + + .mandatory = true, + .default_value.socket = NETADDR_SOCKET_UNSPEC_INIT, + + .from_tlv = dlep_reader_map_identity, + .to_tlv = dlep_writer_map_identity, + }, +}; + +/* DLEP base extension, radio side */ +static struct dlep_extension _ext_dns = { + .id = DLEP_EXTENSION_DNS, + .name = "dns exchange", + + .signals = _signals, + .signal_count = ARRAYSIZE(_signals), + .tlvs = _tlvs, + .tlv_count = ARRAYSIZE(_tlvs), + .if_mapping = _net_mappings, + .if_mapping_count = ARRAYSIZE(_net_mappings), +}; + +/** + * Initialize the base metric DLEP extension + * @return this extension + */ +struct dlep_extension * +dlep_dns_init(void) { + dlep_extension_add(&_ext_dns); + return &_ext_dns; +} + +void +dlep_dns_cleanup(void) { +} diff --git a/src/generic/dlep/ext_l1_statistics/l1_statistics.c b/src/generic/dlep/ext_l1_statistics/l1_statistics.c index 00d28f1a..ace65e1a 100644 --- a/src/generic/dlep/ext_l1_statistics/l1_statistics.c +++ b/src/generic/dlep/ext_l1_statistics/l1_statistics.c @@ -187,8 +187,6 @@ static struct dlep_network_mapping _net_mappings[] = { .length = 8, .scaling = 1, - .mandatory = true, - .from_tlv = _reader_map_frequency, .to_tlv = _writer_map_frequency, }, @@ -198,8 +196,6 @@ static struct dlep_network_mapping _net_mappings[] = { .length = 8, .scaling = 1, - .mandatory = true, - .from_tlv = _reader_map_bandwidth, .to_tlv = _writer_map_bandwidth, }, diff --git a/src/generic/dlep/radio/dlep_radio_interface.c b/src/generic/dlep/radio/dlep_radio_interface.c index 1d47b553..2c072cfe 100644 --- a/src/generic/dlep/radio/dlep_radio_interface.c +++ b/src/generic/dlep/radio/dlep_radio_interface.c @@ -67,6 +67,7 @@ #include #include #include +#include #include #include @@ -80,6 +81,7 @@ static struct oonf_class _interface_class = { static bool _shutting_down; +#if 0 static struct oonf_layer2_origin _l2_origin = { .name = "dlep_radio", .proactive = true, @@ -91,6 +93,7 @@ static struct oonf_layer2_origin _l2_default_origin = { .proactive = false, .priority = OONF_LAYER2_ORIGIN_DEFAULT, }; +#endif /** * Initialize everything for dlep radio interfaces. This function also @@ -111,7 +114,11 @@ dlep_radio_interface_init(void) { dlep_l2_statistics_init(); dlep_radio_attributes_init(); dlep_lid_init(); - + dlep_dns_init(); +#if 0 + oonf_layer2_origin_add(&_l2_origin); + oonf_layer2_origin_add(&_l2_default_origin); +#endif _shutting_down = false; return 0; } @@ -131,6 +138,10 @@ dlep_radio_interface_cleanup(void) { oonf_class_remove(&_interface_class); dlep_radio_session_cleanup(); dlep_extension_cleanup(); +#if 0 + oonf_layer2_origin_remove(&_l2_origin); + oonf_layer2_origin_remove(&_l2_default_origin); +#endif } /** @@ -171,6 +182,7 @@ dlep_radio_get_by_datapath_if(const char *ifname) { struct dlep_radio_if * dlep_radio_add_interface(const char *ifname) { struct dlep_radio_if *interface; + char buffer[64]; interface = dlep_radio_get_by_layer2_if(ifname); if (interface) { @@ -182,7 +194,21 @@ dlep_radio_add_interface(const char *ifname) { return NULL; } - if (dlep_if_add(&interface->interf, ifname, &_l2_origin, &_l2_default_origin, NULL, LOG_DLEP_RADIO, true)) { + snprintf(buffer, sizeof(buffer), "dlep_ra_%s", ifname); + interface->l2_origin.name = strdup(buffer); + interface->l2_origin.priority = OONF_LAYER2_ORIGIN_RELIABLE, + interface->l2_origin.proactive = true; + oonf_layer2_origin_add(&interface->l2_origin); + + snprintf(buffer, sizeof(buffer), "dlep_rad_%s", ifname); + interface->l2_default_origin.name = strdup(buffer); + interface->l2_default_origin.priority = OONF_LAYER2_ORIGIN_UNRELIABLE, + interface->l2_default_origin.proactive = false; + oonf_layer2_origin_add(&interface->l2_origin); + + + if (dlep_if_add(&interface->interf, ifname, &interface->l2_origin, + &interface->l2_default_origin, NULL, LOG_DLEP_RADIO, true)) { oonf_class_free(&_interface_class, interface); return NULL; } diff --git a/src/generic/dlep/router/dlep_router_interface.c b/src/generic/dlep/router/dlep_router_interface.c index bac673f6..81bf08b9 100644 --- a/src/generic/dlep/router/dlep_router_interface.c +++ b/src/generic/dlep/router/dlep_router_interface.c @@ -73,6 +73,7 @@ #include #include #include +#include #include #include @@ -89,6 +90,7 @@ static struct oonf_class _router_if_class = { static bool _shutting_down; +#if 0 static struct oonf_layer2_origin _l2_origin = { .name = "dlep_router", .proactive = true, @@ -100,6 +102,7 @@ static struct oonf_layer2_origin _l2_default_origin = { .proactive = false, .priority = OONF_LAYER2_ORIGIN_UNRELIABLE, }; +#endif static struct oonf_timer_class _connect_to_watchdog_class = { .name = "connect_to watchdog", @@ -125,10 +128,15 @@ dlep_router_interface_init(void) { dlep_l2_statistics_init(); dlep_radio_attributes_init(); dlep_lid_init(); + dlep_dns_init(); _shutting_down = false; +#if 0 oonf_layer2_origin_add(&_l2_origin); + oonf_layer2_origin_add(&_l2_default_origin); +#endif + oonf_timer_add(&_connect_to_watchdog_class); } @@ -149,7 +157,10 @@ dlep_router_interface_cleanup(void) { dlep_base_ip_cleanup(); dlep_router_session_cleanup(); dlep_extension_cleanup(); +#if 0 oonf_layer2_origin_remove(&_l2_origin); + oonf_layer2_origin_remove(&_l2_default_origin); +#endif oonf_timer_remove(&_connect_to_watchdog_class); } @@ -190,6 +201,7 @@ dlep_router_get_by_datapath_if(const char *ifname) { struct dlep_router_if * dlep_router_add_interface(const char *ifname) { struct dlep_router_if *interface; + char buffer[64]; interface = dlep_router_get_by_layer2_if(ifname); if (interface) { @@ -202,7 +214,20 @@ dlep_router_add_interface(const char *ifname) { return NULL; } - if (dlep_if_add(&interface->interf, ifname, &_l2_origin, &_l2_default_origin, _connect_to_if_changed, LOG_DLEP_ROUTER, false)) { + snprintf(buffer, sizeof(buffer), "dlep_rt_%s", ifname); + interface->l2_origin.name = strdup(buffer); + interface->l2_origin.priority = OONF_LAYER2_ORIGIN_RELIABLE, + interface->l2_origin.proactive = true; + oonf_layer2_origin_add(&interface->l2_origin); + + snprintf(buffer, sizeof(buffer), "dlep_rtd_%s", ifname); + interface->l2_default_origin.name = strdup(buffer); + interface->l2_default_origin.priority = OONF_LAYER2_ORIGIN_UNRELIABLE, + interface->l2_default_origin.proactive = false; + oonf_layer2_origin_add(&interface->l2_origin); + + if (dlep_if_add(&interface->interf, ifname, &interface->l2_origin, + &interface->l2_default_origin, _connect_to_if_changed, LOG_DLEP_ROUTER, false)) { oonf_class_free(&_router_if_class, interface); return NULL; } diff --git a/src/generic/dns_query/CMakeLists.txt b/src/generic/dns_query/CMakeLists.txt new file mode 100644 index 00000000..250b894d --- /dev/null +++ b/src/generic/dns_query/CMakeLists.txt @@ -0,0 +1,13 @@ +# set library parameters +SET (name dns_query) + +SET (source dns.c + dns_query.c + ) + +SET (headers dns.h + dns_query.h + ) + +# use generic plugin maker +oonf_create_plugin("${name}" "${source}" "${headers}" "") diff --git a/src/generic/dns_query/dns.c b/src/generic/dns_query/dns.c new file mode 100644 index 00000000..91e483c0 --- /dev/null +++ b/src/generic/dns_query/dns.c @@ -0,0 +1,9941 @@ +/* ========================================================================== + * dns.c - Recursive, Reentrant DNS Resolver. + * -------------------------------------------------------------------------- + * Copyright (c) 2008, 2009, 2010, 2012-2016 William Ahern + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to permit + * persons to whom the Software is furnished to do so, subject to the + * following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN + * NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE + * USE OR OTHER DEALINGS IN THE SOFTWARE. + * ========================================================================== + */ + +#pragma GCC diagnostic ignored "-Wundef" +#pragma GCC diagnostic ignored "-Wexpansion-to-defined" +#pragma GCC diagnostic ignored "-Wswitch-default" +#pragma GCC diagnostic ignored "-Wimplicit-fallthrough" +#pragma GCC diagnostic ignored "-Wstrict-prototypes" +#pragma GCC diagnostic ignored "-Wjump-misses-init" + +#if !defined(__FreeBSD__) && !defined(__sun) +#ifndef _XOPEN_SOURCE +#define _XOPEN_SOURCE 600 +#endif + +#undef _BSD_SOURCE +#define _BSD_SOURCE + +#undef _DARWIN_C_SOURCE +#define _DARWIN_C_SOURCE + +#undef _NETBSD_SOURCE +#define _NETBSD_SOURCE +#endif + +#include /* INT_MAX */ +#include /* offsetof() */ +#ifdef _WIN32 +#define uint32_t unsigned int +#else +#include /* uint32_t */ +#endif +#include /* malloc(3) realloc(3) free(3) rand(3) random(3) arc4random(3) */ +#include /* FILE fopen(3) fclose(3) getc(3) rewind(3) */ +#include /* memcpy(3) strlen(3) memmove(3) memchr(3) memcmp(3) strchr(3) strsep(3) strcspn(3) */ +#include /* strcasecmp(3) strncasecmp(3) */ +#include /* isspace(3) isdigit(3) */ +#include /* time_t time(2) difftime(3) */ +#include /* SIGPIPE sigemptyset(3) sigaddset(3) sigpending(2) sigprocmask(2) pthread_sigmask(3) sigtimedwait(2) */ +#include /* errno EINVAL ENOENT */ +#undef NDEBUG +#include /* assert(3) */ + +#if _WIN32 +#ifndef FD_SETSIZE +#define FD_SETSIZE 256 +#endif +#include +#include +#else +#include /* FD_SETSIZE socklen_t */ +#include /* FD_ZERO FD_SET fd_set select(2) */ +#include /* AF_INET AF_INET6 AF_UNIX struct sockaddr struct sockaddr_in struct sockaddr_in6 socket(2) */ +#if defined(AF_UNIX) +#include /* struct sockaddr_un */ +#endif +#include /* F_SETFD F_GETFL F_SETFL O_NONBLOCK fcntl(2) */ +#include /* _POSIX_THREADS gethostname(3) close(2) */ +#include /* POLLIN POLLOUT */ +#include /* struct sockaddr_in struct sockaddr_in6 */ +#include /* inet_pton(3) inet_ntop(3) htons(3) ntohs(3) */ +#include /* struct addrinfo */ +#endif + +#include + + +/* + * C O M P I L E R V E R S I O N & F E A T U R E D E T E C T I O N + * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#define DNS_GNUC_2VER(M, m, p) (((M) * 10000) + ((m) * 100) + (p)) +#define DNS_GNUC_PREREQ(M, m, p) (__GNUC__ > 0 && DNS_GNUC_2VER(__GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__) >= DNS_GNUC_2VER((M), (m), (p))) + +#define DNS_MSC_2VER(M, m, p) ((((M) + 6) * 10000000) + ((m) * 1000000) + (p)) +#define DNS_MSC_PREREQ(M, m, p) (_MSC_VER_FULL > 0 && _MSC_VER_FULL >= DNS_MSC_2VER((M), (m), (p))) + +#define DNS_SUNPRO_PREREQ(M, m, p) (__SUNPRO_C > 0 && __SUNPRO_C >= 0x ## M ## m ## p) + +#if defined __has_builtin +#define dns_has_builtin(x) __has_builtin(x) +#else +#define dns_has_builtin(x) 0 +#endif + +#if defined __has_extension +#define dns_has_extension(x) __has_extension(x) +#else +#define dns_has_extension(x) 0 +#endif + +#ifndef HAVE___ASSUME +#define HAVE___ASSUME DNS_MSC_PREREQ(8,0,0) +#endif + +#ifndef HAVE___BUILTIN_TYPES_COMPATIBLE_P +#define HAVE___BUILTIN_TYPES_COMPATIBLE_P (DNS_GNUC_PREREQ(3,1,1) || __clang__) +#endif + +#ifndef HAVE___BUILTIN_UNREACHABLE +#define HAVE___BUILTIN_UNREACHABLE (DNS_GNUC_PREREQ(4,5,0) || dns_has_builtin(__builtin_unreachable)) +#endif + +#ifndef HAVE_PRAGMA_MESSAGE +#define HAVE_PRAGMA_MESSAGE (DNS_GNUC_PREREQ(4,4,0) || __clang__ || _MSC_VER) +#endif + + +/* + * C O M P I L E R A N N O T A T I O N S + * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#if __GNUC__ +#define DNS_NOTUSED __attribute__((unused)) +#define DNS_NORETURN __attribute__((noreturn)) +#else +#define DNS_NOTUSED +#define DNS_NORETURN +#endif + +#if __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunused-parameter" +#pragma clang diagnostic ignored "-Wmissing-field-initializers" +#elif DNS_GNUC_PREREQ(4,6,0) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +#pragma GCC diagnostic ignored "-Wmissing-field-initializers" +#endif + + +/* + * S T A N D A R D M A C R O S + * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#if HAVE___BUILTIN_TYPES_COMPATIBLE_P +#define dns_same_type(a, b, def) __builtin_types_compatible_p(__typeof__ (a), __typeof__ (b)) +#else +#define dns_same_type(a, b, def) (def) +#endif +#define dns_isarray(a) (!dns_same_type((a), (&(a)[0]), 0)) +/* NB: "_" field silences Sun Studio "zero-sized struct/union" error diagnostic */ +#define dns_inline_assert(cond) ((void)(sizeof (struct { int:-!(cond); int _; }))) + +#if HAVE___ASSUME +#define dns_assume(cond) __assume(cond) +#elif HAVE___BUILTIN_UNREACHABLE +#define dns_assume(cond) do { if (!(cond)) __builtin_unreachable(); } while (0) +#else +#define dns_assume(cond) do { (void)(cond); } while (0) +#endif + +#ifndef lengthof +#define lengthof(a) (dns_inline_assert(dns_isarray(a)), (sizeof (a) / sizeof (a)[0])) +#endif + +#ifndef endof +#define endof(a) (dns_inline_assert(dns_isarray(a)), &(a)[lengthof((a))]) +#endif + + +/* + * M I S C E L L A N E O U S C O M P A T + * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#if _WIN32 || _WIN64 +#define PRIuZ "Iu" +#else +#define PRIuZ "zu" +#endif + +#ifndef DNS_THREAD_SAFE +#if (defined _REENTRANT || defined _THREAD_SAFE) && _POSIX_THREADS > 0 +#define DNS_THREAD_SAFE 1 +#else +#define DNS_THREAD_SAFE 0 +#endif +#endif + +#ifndef HAVE__STATIC_ASSERT +#define HAVE__STATIC_ASSERT \ + (dns_has_extension(c_static_assert) || DNS_GNUC_PREREQ(4,6,0) || \ + __C11FEATURES__ || __STDC_VERSION__ >= 201112L) +#endif + +#ifndef HAVE_STATIC_ASSERT +#if DNS_GNUC_PREREQ(0,0,0) && !DNS_GNUC_PREREQ(4,6,0) +#define HAVE_STATIC_ASSERT 0 /* glibc doesn't check GCC version */ +#else +#define HAVE_STATIC_ASSERT (defined static_assert) +#endif +#endif + +#if HAVE_STATIC_ASSERT +#define dns_static_assert(cond, msg) static_assert(cond, msg) +#elif HAVE__STATIC_ASSERT +#define dns_static_assert(cond, msg) _Static_assert(cond, msg) +#else +#define dns_static_assert(cond, msg) extern char DNS_PP_XPASTE(dns_assert_, __LINE__)[sizeof (int[1 - 2*!(cond)])] +#endif + + +/* + * D E B U G M A C R O S + * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +int *dns_debug_p(void) { + static int debug; + + return &debug; +} /* dns_debug_p() */ + +#if DNS_DEBUG + +#undef DNS_DEBUG +#define DNS_DEBUG dns_debug + +#define DNS_SAY_(fmt, ...) \ + do { if (DNS_DEBUG > 0) fprintf(stderr, fmt "%.1s", __func__, __LINE__, __VA_ARGS__); } while (0) +#define DNS_SAY(...) DNS_SAY_("@@ (%s:%d) " __VA_ARGS__, "\n") +#define DNS_HAI DNS_SAY("HAI") + +#define DNS_SHOW_(P, fmt, ...) do { \ + if (DNS_DEBUG > 1) { \ + fprintf(stderr, "@@ BEGIN * * * * * * * * * * * *\n"); \ + fprintf(stderr, "@@ " fmt "%.0s\n", __VA_ARGS__); \ + dns_p_dump((P), stderr); \ + fprintf(stderr, "@@ END * * * * * * * * * * * * *\n\n"); \ + } \ +} while (0) + +#define DNS_SHOW(...) DNS_SHOW_(__VA_ARGS__, "") + +#else /* !DNS_DEBUG */ + +#undef DNS_DEBUG +#define DNS_DEBUG 0 + +#define DNS_SAY(...) +#define DNS_HAI +#define DNS_SHOW(...) + +#endif /* DNS_DEBUG */ + +#define DNS_CARP(...) DNS_SAY(__VA_ARGS__) + + +/* + * V E R S I O N R O U T I N E S + * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +const char *dns_vendor(void) { + return DNS_VENDOR; +} /* dns_vendor() */ + + +int dns_v_rel(void) { + return DNS_V_REL; +} /* dns_v_rel() */ + + +int dns_v_abi(void) { + return DNS_V_ABI; +} /* dns_v_abi() */ + + +int dns_v_api(void) { + return DNS_V_API; +} /* dns_v_api() */ + + +/* + * E R R O R R O U T I N E S + * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#if _WIN32 + +#define DNS_EINTR WSAEINTR +#define DNS_EINPROGRESS WSAEINPROGRESS +#define DNS_EISCONN WSAEISCONN +#define DNS_EWOULDBLOCK WSAEWOULDBLOCK +#define DNS_EALREADY WSAEALREADY +#define DNS_EAGAIN EAGAIN +#define DNS_ETIMEDOUT WSAETIMEDOUT + +#define dns_syerr() ((int)GetLastError()) +#define dns_soerr() ((int)WSAGetLastError()) + +#else + +#define DNS_EINTR EINTR +#define DNS_EINPROGRESS EINPROGRESS +#define DNS_EISCONN EISCONN +#define DNS_EWOULDBLOCK EWOULDBLOCK +#define DNS_EALREADY EALREADY +#define DNS_EAGAIN EAGAIN +#define DNS_ETIMEDOUT ETIMEDOUT + +#define dns_syerr() errno +#define dns_soerr() errno + +#endif + + +const char *dns_strerror(int error) { + switch (error) { + case DNS_ENOBUFS: + return "DNS packet buffer too small"; + case DNS_EILLEGAL: + return "Illegal DNS RR name or data"; + case DNS_EORDER: + return "Attempt to push RR out of section order"; + case DNS_ESECTION: + return "Invalid section specified"; + case DNS_EUNKNOWN: + return "Unknown DNS error"; + case DNS_EADDRESS: + return "Invalid textual address form"; + case DNS_ENOQUERY: + return "Bad execution state (missing query packet)"; + case DNS_ENOANSWER: + return "Bad execution state (missing answer packet)"; + case DNS_EFETCHED: + return "Answer already fetched"; + case DNS_ESERVICE: + return "The service passed was not recognized for the specified socket type"; + case DNS_ENONAME: + return "The name does not resolve for the supplied parameters"; + case DNS_EFAIL: + return "A non-recoverable error occurred when attempting to resolve the name"; + default: + return strerror(error); + } /* switch() */ +} /* dns_strerror() */ + + +/* + * A T O M I C R O U T I N E S + * + * Use GCC's __atomic built-ins if possible. Unlike the __sync built-ins, we + * can use the preprocessor to detect API and, more importantly, ISA + * support. We want to avoid linking headaches where the API depends on an + * external library if the ISA (e.g. i386) doesn't support lockless + * operation. + * + * TODO: Support C11's atomic API. Although that may require some finesse + * with how we define some public types, such as dns_atomic_t and struct + * dns_resolv_conf. + * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#ifndef HAVE___ATOMIC_FETCH_ADD +#define HAVE___ATOMIC_FETCH_ADD (defined __ATOMIC_RELAXED) +#endif + +#ifndef HAVE___ATOMIC_FETCH_SUB +#define HAVE___ATOMIC_FETCH_SUB HAVE___ATOMIC_FETCH_ADD +#endif + +#ifndef DNS_ATOMIC_FETCH_ADD +#if HAVE___ATOMIC_FETCH_ADD && __GCC_ATOMIC_LONG_LOCK_FREE == 2 +#define DNS_ATOMIC_FETCH_ADD(i) __atomic_fetch_add((i), 1, __ATOMIC_RELAXED) +#else +#pragma message("no atomic_fetch_add available") +#define DNS_ATOMIC_FETCH_ADD(i) ((*(i))++) +#endif +#endif + +#ifndef DNS_ATOMIC_FETCH_SUB +#if HAVE___ATOMIC_FETCH_SUB && __GCC_ATOMIC_LONG_LOCK_FREE == 2 +#define DNS_ATOMIC_FETCH_SUB(i) __atomic_fetch_sub((i), 1, __ATOMIC_RELAXED) +#else +#pragma message("no atomic_fetch_sub available") +#define DNS_ATOMIC_FETCH_SUB(i) ((*(i))--) +#endif +#endif + +static inline unsigned dns_atomic_fetch_add(dns_atomic_t *i) { + return DNS_ATOMIC_FETCH_ADD(i); +} /* dns_atomic_fetch_add() */ + + +static inline unsigned dns_atomic_fetch_sub(dns_atomic_t *i) { + return DNS_ATOMIC_FETCH_SUB(i); +} /* dns_atomic_fetch_sub() */ + + +/* + * C R Y P T O R O U T I N E S + * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +/* + * P R N G + */ + +#ifndef DNS_RANDOM +#if defined(HAVE_ARC4RANDOM) \ + || defined(__OpenBSD__) \ + || defined(__FreeBSD__) \ + || defined(__NetBSD__) \ + || defined(__APPLE__) +#define DNS_RANDOM arc4random +#elif __linux +#define DNS_RANDOM random +#else +#define DNS_RANDOM rand +#endif +#endif + +#define DNS_RANDOM_arc4random 1 +#define DNS_RANDOM_random 2 +#define DNS_RANDOM_rand 3 +#define DNS_RANDOM_RAND_bytes 4 + +#define DNS_RANDOM_OPENSSL (DNS_RANDOM_RAND_bytes == DNS_PP_XPASTE(DNS_RANDOM_, DNS_RANDOM)) + +#if DNS_RANDOM_OPENSSL +#include +#endif + +static unsigned dns_random_(void) { +#if DNS_RANDOM_OPENSSL + unsigned r; + _Bool ok; + + ok = (1 == RAND_bytes((unsigned char *)&r, sizeof r)); + assert(ok && "1 == RAND_bytes()"); + + return r; +#else + return DNS_RANDOM(); +#endif +} /* dns_random_() */ + +dns_random_f **dns_random_p(void) { + static dns_random_f *random_f = &dns_random_; + + return &random_f; +} /* dns_random_p() */ + + +/* + * P E R M U T A T I O N G E N E R A T O R + */ + +#define DNS_K_TEA_KEY_SIZE 16 +#define DNS_K_TEA_BLOCK_SIZE 8 +#define DNS_K_TEA_CYCLES 32 +#define DNS_K_TEA_MAGIC 0x9E3779B9U + +struct dns_k_tea { + uint32_t key[DNS_K_TEA_KEY_SIZE / sizeof (uint32_t)]; + unsigned cycles; +}; /* struct dns_k_tea */ + + +static void dns_k_tea_init(struct dns_k_tea *tea, uint32_t key[], unsigned cycles) { + memcpy(tea->key, key, sizeof tea->key); + + tea->cycles = (cycles)? cycles : DNS_K_TEA_CYCLES; +} /* dns_k_tea_init() */ + + +static void dns_k_tea_encrypt(struct dns_k_tea *tea, uint32_t v[], uint32_t *w) { + uint32_t y, z, sum, n; + + y = v[0]; + z = v[1]; + sum = 0; + + for (n = 0; n < tea->cycles; n++) { + sum += DNS_K_TEA_MAGIC; + y += ((z << 4) + tea->key[0]) ^ (z + sum) ^ ((z >> 5) + tea->key[1]); + z += ((y << 4) + tea->key[2]) ^ (y + sum) ^ ((y >> 5) + tea->key[3]); + } + + w[0] = y; + w[1] = z; + + return /* void */; +} /* dns_k_tea_encrypt() */ + + +/* + * Permutation generator, based on a Luby-Rackoff Feistel construction. + * + * Specifically, this is a generic balanced Feistel block cipher using TEA + * (another block cipher) as the pseudo-random function, F. At best it's as + * strong as F (TEA), notwithstanding the seeding. F could be AES, SHA-1, or + * perhaps Bernstein's Salsa20 core; I am naively trying to keep things + * simple. + * + * The generator can create a permutation of any set of numbers, as long as + * the size of the set is an even power of 2. This limitation arises either + * out of an inherent property of balanced Feistel constructions, or by my + * own ignorance. I'll tackle an unbalanced construction after I wrap my + * head around Schneier and Kelsey's paper. + * + * CAVEAT EMPTOR. IANAC. + */ +#define DNS_K_PERMUTOR_ROUNDS 8 + +struct dns_k_permutor { + unsigned stepi, length, limit; + unsigned shift, mask, rounds; + + struct dns_k_tea tea; +}; /* struct dns_k_permutor */ + + +static inline unsigned dns_k_permutor_powof(unsigned n) { + unsigned m, i = 0; + + for (m = 1; m < n; m <<= 1, i++) + ;; + + return i; +} /* dns_k_permutor_powof() */ + +static void dns_k_permutor_init(struct dns_k_permutor *p, unsigned low, unsigned high) { + uint32_t key[DNS_K_TEA_KEY_SIZE / sizeof (uint32_t)]; + unsigned width, i; + + p->stepi = 0; + + p->length = (high - low) + 1; + p->limit = high; + + width = dns_k_permutor_powof(p->length); + width += width % 2; + + p->shift = width / 2; + p->mask = (1U << p->shift) - 1; + p->rounds = DNS_K_PERMUTOR_ROUNDS; + + for (i = 0; i < lengthof(key); i++) + key[i] = dns_random(); + + dns_k_tea_init(&p->tea, key, 0); + + return /* void */; +} /* dns_k_permutor_init() */ + + +static unsigned dns_k_permutor_F(struct dns_k_permutor *p, unsigned k, unsigned x) { + uint32_t in[DNS_K_TEA_BLOCK_SIZE / sizeof (uint32_t)], out[DNS_K_TEA_BLOCK_SIZE / sizeof (uint32_t)]; + + memset(in, '\0', sizeof in); + + in[0] = k; + in[1] = x; + + dns_k_tea_encrypt(&p->tea, in, out); + + return p->mask & out[0]; +} /* dns_k_permutor_F() */ + + +static unsigned dns_k_permutor_E(struct dns_k_permutor *p, unsigned n) { + unsigned l[2], r[2]; + unsigned i; + + i = 0; + l[i] = p->mask & (n >> p->shift); + r[i] = p->mask & (n >> 0); + + do { + l[(i + 1) % 2] = r[i % 2]; + r[(i + 1) % 2] = l[i % 2] ^ dns_k_permutor_F(p, i, r[i % 2]); + + i++; + } while (i < p->rounds - 1); + + return ((l[i % 2] & p->mask) << p->shift) | ((r[i % 2] & p->mask) << 0); +} /* dns_k_permutor_E() */ + + +DNS_NOTUSED static unsigned dns_k_permutor_D(struct dns_k_permutor *p, unsigned n) { + unsigned l[2], r[2]; + unsigned i; + + i = p->rounds - 1; + l[i % 2] = p->mask & (n >> p->shift); + r[i % 2] = p->mask & (n >> 0); + + do { + i--; + + r[i % 2] = l[(i + 1) % 2]; + l[i % 2] = r[(i + 1) % 2] ^ dns_k_permutor_F(p, i, l[(i + 1) % 2]); + } while (i > 0); + + return ((l[i % 2] & p->mask) << p->shift) | ((r[i % 2] & p->mask) << 0); +} /* dns_k_permutor_D() */ + + +static unsigned dns_k_permutor_step(struct dns_k_permutor *p) { + unsigned n; + + do { + n = dns_k_permutor_E(p, p->stepi++); + } while (n >= p->length); + + return n + (p->limit + 1 - p->length); +} /* dns_k_permutor_step() */ + + +/* + * Simple permutation box. Useful for shuffling rrsets from an iterator. + * Uses AES s-box to provide good diffusion. + * + * Seems to pass muster under runs test. + * + * $ for i in 0 1 2 3 4 5 6 7 8 9; do ./dns shuffle-16 > /tmp/out; done + * $ R -q -f /dev/stdin 2>/dev/null <<-EOF | awk '/p-value/{ print $8 }' + * library(lawstat) + * runs.test(scan(file="/tmp/out")) + * EOF + */ +static unsigned short dns_k_shuffle16(unsigned short n, unsigned s) { + static const unsigned char sbox[256] = + { 0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, + 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76, + 0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, + 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0, + 0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, + 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15, + 0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, + 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75, + 0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, + 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84, + 0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, + 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf, + 0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, + 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8, + 0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, + 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2, + 0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, + 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73, + 0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, + 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb, + 0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, + 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79, + 0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, + 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08, + 0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, + 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a, + 0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, + 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e, + 0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, + 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf, + 0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, + 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16 }; + unsigned char a, b; + unsigned i; + + a = 0xff & (n >> 0); + b = 0xff & (n >> 8); + + for (i = 0; i < 4; i++) { + a ^= 0xff & s; + a = sbox[a] ^ b; + b = sbox[b] ^ a; + s >>= 8; + } + + return ((0xff00 & (a << 8)) | (0x00ff & (b << 0))); +} /* dns_k_shuffle16() */ + +/* + * S T A T E M A C H I N E R O U T I N E S + * + * Application code should define DNS_SM_RESTORE and DNS_SM_SAVE, and the + * local variable pc. + * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#define DNS_SM_ENTER \ + do { \ + static const int pc0 = __LINE__; \ + DNS_SM_RESTORE; \ + switch (pc0 + pc) { \ + case __LINE__: (void)0 + +#define DNS_SM_SAVE_AND_DO(do_statement) \ + do { \ + pc = __LINE__ - pc0; \ + DNS_SM_SAVE; \ + do_statement; \ + case __LINE__: (void)0; \ + } while (0) + +#define DNS_SM_YIELD(rv) \ + DNS_SM_SAVE_AND_DO(return (rv)) + +#define DNS_SM_EXIT \ + do { goto leave; } while (0) + +#define DNS_SM_LEAVE \ + leave: (void)0; \ + DNS_SM_SAVE_AND_DO(break); \ + } \ + } while (0) + +/* + * U T I L I T Y R O U T I N E S + * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#define DNS_MAXINTERVAL 300 + +struct dns_clock { + time_t sample, elapsed; +}; /* struct dns_clock */ + +static void dns_begin(struct dns_clock *clk) { + clk->sample = time(0); + clk->elapsed = 0; +} /* dns_begin() */ + +static time_t dns_elapsed(struct dns_clock *clk) { + time_t curtime; + + if ((time_t)-1 == time(&curtime)) + return clk->elapsed; + + if (curtime > clk->sample) + clk->elapsed += (time_t)DNS_PP_MIN(difftime(curtime, clk->sample), DNS_MAXINTERVAL); + + clk->sample = curtime; + + return clk->elapsed; +} /* dns_elapsed() */ + + +DNS_NOTUSED static size_t dns_strnlen(const char *src, size_t m) { + size_t n = 0; + + while (*src++ && n < m) + ++n; + + return n; +} /* dns_strnlen() */ + + +DNS_NOTUSED static size_t dns_strnlcpy(char *dst, size_t lim, const char *src, size_t max) { + size_t len = dns_strnlen(src, max), n; + + if (lim > 0) { + n = DNS_PP_MIN(lim - 1, len); + memcpy(dst, src, n); + dst[n] = '\0'; + } + + return len; +} /* dns_strnlcpy() */ + + +#define DNS_HAVE_SOCKADDR_UN (defined AF_UNIX && !defined _WIN32) + +static size_t dns_af_len(int af) { + static const size_t table[AF_MAX] = { + [AF_INET6] = sizeof (struct sockaddr_in6), + [AF_INET] = sizeof (struct sockaddr_in), +#if DNS_HAVE_SOCKADDR_UN + [AF_UNIX] = sizeof (struct sockaddr_un), +#endif + }; + + return table[af]; +} /* dns_af_len() */ + +#define dns_sa_family(sa) (((struct sockaddr *)(sa))->sa_family) + +#define dns_sa_len(sa) dns_af_len(dns_sa_family(sa)) + + +#define DNS_SA_NOPORT &dns_sa_noport +static unsigned short dns_sa_noport; + +static unsigned short *dns_sa_port(int af, void *sa) { + switch (af) { + case AF_INET6: + return &((struct sockaddr_in6 *)sa)->sin6_port; + case AF_INET: + return &((struct sockaddr_in *)sa)->sin_port; + default: + return DNS_SA_NOPORT; + } +} /* dns_sa_port() */ + + +static void *dns_sa_addr(int af, const void *sa, socklen_t *size) { + switch (af) { + case AF_INET6: { + struct in6_addr *in6 = &((struct sockaddr_in6 *)sa)->sin6_addr; + + if (size) + *size = sizeof *in6; + + return in6; + } + case AF_INET: { + struct in_addr *in = &((struct sockaddr_in *)sa)->sin_addr; + + if (size) + *size = sizeof *in; + + return in; + } + default: + if (size) + *size = 0; + + return 0; + } +} /* dns_sa_addr() */ + + +#if DNS_HAVE_SOCKADDR_UN +#define DNS_SUNPATHMAX (sizeof ((struct sockaddr_un *)0)->sun_path) +#endif + +DNS_NOTUSED static void *dns_sa_path(void *sa, socklen_t *size) { + switch (dns_sa_family(sa)) { +#if DNS_HAVE_SOCKADDR_UN + case AF_UNIX: { + char *path = ((struct sockaddr_un *)sa)->sun_path; + + if (size) + *size = dns_strnlen(path, DNS_SUNPATHMAX); + + return path; + } +#endif + default: + if (size) + *size = 0; + + return NULL; + } +} /* dns_sa_path() */ + + +static int dns_sa_cmp(void *a, void *b) { + int cmp, af; + + if ((cmp = dns_sa_family(a) - dns_sa_family(b))) + return cmp; + + switch ((af = dns_sa_family(a))) { + case AF_INET: { + struct in_addr *a4, *b4; + + if ((cmp = htons(*dns_sa_port(af, a)) - htons(*dns_sa_port(af, b)))) + return cmp; + + a4 = dns_sa_addr(af, a, NULL); + b4 = dns_sa_addr(af, b, NULL); + + if (ntohl(a4->s_addr) < ntohl(b4->s_addr)) + return -1; + if (ntohl(a4->s_addr) > ntohl(b4->s_addr)) + return 1; + + return 0; + } + case AF_INET6: { + struct in6_addr *a6, *b6; + size_t i; + + if ((cmp = htons(*dns_sa_port(af, a)) - htons(*dns_sa_port(af, b)))) + return cmp; + + a6 = dns_sa_addr(af, a, NULL); + b6 = dns_sa_addr(af, b, NULL); + + /* XXX: do we need to use in6_clearscope()? */ + for (i = 0; i < sizeof a6->s6_addr; i++) { + if ((cmp = a6->s6_addr[i] - b6->s6_addr[i])) + return cmp; + } + + return 0; + } +#if DNS_HAVE_SOCKADDR_UN + case AF_UNIX: { + char a_path[DNS_SUNPATHMAX + 1], b_path[sizeof a_path]; + + dns_strnlcpy(a_path, sizeof a_path, dns_sa_path(a, NULL), DNS_SUNPATHMAX); + dns_strnlcpy(b_path, sizeof b_path, dns_sa_path(b, NULL), DNS_SUNPATHMAX); + + return strcmp(a_path, b_path); + } +#endif + default: + return -1; + } +} /* dns_sa_cmp() */ + + +#if _WIN32 +static int dns_inet_pton(int af, const void *src, void *dst) { + union { struct sockaddr_in sin; struct sockaddr_in6 sin6; } u; + + u.sin.sin_family = af; + + if (0 != WSAStringToAddressA((void *)src, af, (void *)0, (struct sockaddr *)&u, &(int){ sizeof u })) + return -1; + + switch (af) { + case AF_INET6: + *(struct in6_addr *)dst = u.sin6.sin6_addr; + + return 1; + case AF_INET: + *(struct in_addr *)dst = u.sin.sin_addr; + + return 1; + default: + return 0; + } +} /* dns_inet_pton() */ + +static const char *dns_inet_ntop(int af, const void *src, void *dst, unsigned long lim) { + union { struct sockaddr_in sin; struct sockaddr_in6 sin6; } u; + + /* NOTE: WSAAddressToString will print .sin_port unless zeroed. */ + memset(&u, 0, sizeof u); + + u.sin.sin_family = af; + + switch (af) { + case AF_INET6: + u.sin6.sin6_addr = *(struct in6_addr *)src; + break; + case AF_INET: + u.sin.sin_addr = *(struct in_addr *)src; + + break; + default: + return 0; + } + + if (0 != WSAAddressToStringA((struct sockaddr *)&u, dns_sa_len(&u), (void *)0, dst, &lim)) + return 0; + + return dst; +} /* dns_inet_ntop() */ +#else +#define dns_inet_pton(...) inet_pton(__VA_ARGS__) +#define dns_inet_ntop(...) inet_ntop(__VA_ARGS__) +#endif + + +static dns_error_t dns_pton(int af, const void *src, void *dst) { + switch (dns_inet_pton(af, src, dst)) { + case 1: + return 0; + case -1: + return dns_soerr(); + default: + return DNS_EADDRESS; + } +} /* dns_pton() */ + + +static dns_error_t dns_ntop(int af, const void *src, void *dst, unsigned long lim) { + return (dns_inet_ntop(af, src, dst, lim))? 0 : dns_soerr(); +} /* dns_ntop() */ + + +size_t dns_strlcpy(char *dst, const char *src, size_t lim) { + char *d = dst; + char *e = &dst[lim]; + const char *s = src; + + if (d < e) { + do { + if ('\0' == (*d++ = *s++)) + return s - src - 1; + } while (d < e); + + d[-1] = '\0'; + } + + while (*s++ != '\0') + ;; + + return s - src - 1; +} /* dns_strlcpy() */ + + +size_t dns_strlcat(char *dst, const char *src, size_t lim) { + char *d = memchr(dst, '\0', lim); + char *e = &dst[lim]; + const char *s = src; + const char *p; + + if (d && d < e) { + do { + if ('\0' == (*d++ = *s++)) + return d - dst - 1; + } while (d < e); + + d[-1] = '\0'; + } + + p = s; + + while (*s++ != '\0') + ;; + + return lim + (s - p - 1); +} /* dns_strlcat() */ + + +#if _WIN32 + +static char *dns_strsep(char **sp, const char *delim) { + char *p; + + if (!(p = *sp)) + return 0; + + *sp += strcspn(p, delim); + + if (**sp != '\0') { + **sp = '\0'; + ++*sp; + } else + *sp = NULL; + + return p; +} /* dns_strsep() */ + +#else +#define dns_strsep(...) strsep(__VA_ARGS__) +#endif + + +#if _WIN32 +#define strcasecmp(...) _stricmp(__VA_ARGS__) +#define strncasecmp(...) _strnicmp(__VA_ARGS__) +#endif + + +static inline _Bool dns_isalpha(unsigned char c) { + return isalpha(c); +} /* dns_isalpha() */ + +static inline _Bool dns_isdigit(unsigned char c) { + return isdigit(c); +} /* dns_isdigit() */ + +static inline _Bool dns_isalnum(unsigned char c) { + return isalnum(c); +} /* dns_isalnum() */ + +static inline _Bool dns_isspace(unsigned char c) { + return isspace(c); +} /* dns_isspace() */ + + +static int dns_poll(int fd, short events, int timeout) { + fd_set rset, wset; + + if (!events) + return 0; + + assert(fd >= 0 && (unsigned)fd < FD_SETSIZE); + + FD_ZERO(&rset); + FD_ZERO(&wset); + + if (events & DNS_POLLIN) + FD_SET(fd, &rset); + + if (events & DNS_POLLOUT) + FD_SET(fd, &wset); + + select(fd + 1, &rset, &wset, 0, (timeout >= 0)? &(struct timeval){ timeout, 0 } : NULL); + + return 0; +} /* dns_poll() */ + + +#if !_WIN32 +DNS_NOTUSED static int dns_sigmask(int how, const sigset_t *set, sigset_t *oset) { +#if DNS_THREAD_SAFE + return pthread_sigmask(how, set, oset); +#else + return (0 == sigprocmask(how, set, oset))? 0 : errno; +#endif +} /* dns_sigmask() */ +#endif + + +static long dns_send(int fd, const void *src, size_t lim, int flags) { +#if _WIN32 || !defined SIGPIPE || defined SO_NOSIGPIPE + return send(fd, src, lim, flags); +#elif defined MSG_NOSIGNAL + return send(fd, src, lim, flags|MSG_NOSIGNAL); +#elif _POSIX_REALTIME_SIGNALS > 0 /* require sigtimedwait */ + /* + * SIGPIPE handling similar to the approach described in + * http://krokisplace.blogspot.com/2010/02/suppressing-sigpipe-in-library.html + */ + sigset_t pending, blocked, piped; + long count; + int saved, error; + + sigemptyset(&pending); + sigpending(&pending); + + if (!sigismember(&pending, SIGPIPE)) { + sigemptyset(&piped); + sigaddset(&piped, SIGPIPE); + sigemptyset(&blocked); + + if ((error = dns_sigmask(SIG_BLOCK, &piped, &blocked))) + goto error; + } + + count = send(fd, src, lim, flags); + + if (!sigismember(&pending, SIGPIPE)) { + saved = errno; + + if (count == -1 && errno == EPIPE) { + while (-1 == sigtimedwait(&piped, NULL, &(struct timespec){ 0, 0 }) && errno == EINTR) + ;; + } + + if ((error = dns_sigmask(SIG_SETMASK, &blocked, NULL))) + goto error; + + errno = saved; + } + + return count; +error: + errno = error; + + return -1; +#else +#error "unable to suppress SIGPIPE" + return send(fd, src, lim, flags); +#endif +} /* dns_send() */ + + +#define DNS_FOPEN_STDFLAGS "rwabt+" + +static dns_error_t dns_fopen_addflag(char *dst, const char *src, size_t lim, int fc) { + char *p = dst, *pe = dst + lim; + + /* copy standard flags */ + while (*src && strchr(DNS_FOPEN_STDFLAGS, *src)) { + if (!(p < pe)) + return ENOMEM; + *p++ = *src++; + } + + /* append flag to standard flags */ + if (!(p < pe)) + return ENOMEM; + *p++ = fc; + + /* copy remaining mode string, including '\0' */ + do { + if (!(p < pe)) + return ENOMEM; + } while ((*p++ = *src++)); + + return 0; +} /* dns_fopen_addflag() */ + +static FILE *dns_fopen(const char *path, const char *mode, dns_error_t *_error) { + FILE *fp; + char mode_cloexec[32]; + int error; + + assert(path && mode && *mode); + if (!*path) { + error = EINVAL; + goto error; + } + +#if _WIN32 || _WIN64 + if ((error = dns_fopen_addflag(mode_cloexec, mode, sizeof mode_cloexec, 'N'))) + goto error; + if (!(fp = fopen(path, mode_cloexec))) + goto syerr; +#else + if ((error = dns_fopen_addflag(mode_cloexec, mode, sizeof mode_cloexec, 'e'))) + goto error; + if (!(fp = fopen(path, mode_cloexec))) { + if (errno != EINVAL) + goto syerr; + if (!(fp = fopen(path, mode))) + goto syerr; + } +#endif + + return fp; +syerr: + error = dns_syerr(); +error: + *_error = error; + + return NULL; +} /* dns_fopen() */ + + +/* + * F I X E D - S I Z E D B U F F E R R O U T I N E S + * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#define DNS_B_INIT(src, n) { \ + (unsigned char *)(src), \ + (unsigned char *)(src), \ + (unsigned char *)(src) + (n), \ +} + +#define DNS_B_FROM(src, n) DNS_B_INIT((src), (n)) +#define DNS_B_INTO(src, n) DNS_B_INIT((src), (n)) + +struct dns_buf { + const unsigned char *base; + unsigned char *p; + const unsigned char *pe; + dns_error_t error; + size_t overflow; +}; /* struct dns_buf */ + +static inline size_t +dns_b_tell(struct dns_buf *b) +{ + return b->p - b->base; +} + +static inline dns_error_t +dns_b_setoverflow(struct dns_buf *b, size_t n, dns_error_t error) +{ + b->overflow += n; + return b->error = error; +} + +DNS_NOTUSED static struct dns_buf * +dns_b_into(struct dns_buf *b, void *src, size_t n) +{ + *b = (struct dns_buf)DNS_B_INTO(src, n); + + return b; +} + +static dns_error_t +dns_b_putc(struct dns_buf *b, unsigned char uc) +{ + if (!(b->p < b->pe)) + return dns_b_setoverflow(b, 1, DNS_ENOBUFS); + + *b->p++ = uc; + + return 0; +} + +static dns_error_t +dns_b_pputc(struct dns_buf *b, unsigned char uc, size_t p) +{ + size_t pe = b->pe - b->base; + if (pe <= p) + return dns_b_setoverflow(b, p - pe + 1, DNS_ENOBUFS); + + *((unsigned char *)b->base + p) = uc; + + return 0; +} + +static inline dns_error_t +dns_b_put16(struct dns_buf *b, uint16_t u) +{ + return dns_b_putc(b, u >> 8), dns_b_putc(b, u >> 0); +} + +static inline dns_error_t +dns_b_pput16(struct dns_buf *b, uint16_t u, size_t p) +{ + if (dns_b_pputc(b, u >> 8, p) || dns_b_pputc(b, u >> 0, p + 1)) + return b->error; + + return 0; +} + +DNS_NOTUSED static inline dns_error_t +dns_b_put32(struct dns_buf *b, uint32_t u) +{ + return dns_b_putc(b, u >> 24), dns_b_putc(b, u >> 16), + dns_b_putc(b, u >> 8), dns_b_putc(b, u >> 0); +} + +static dns_error_t +dns_b_put(struct dns_buf *b, const void *src, size_t len) +{ + size_t n = DNS_PP_MIN((size_t)(b->pe - b->p), len); + + memcpy(b->p, src, n); + b->p += n; + + if (n < len) + return dns_b_setoverflow(b, len - n, DNS_ENOBUFS); + + return 0; +} + +static dns_error_t +dns_b_puts(struct dns_buf *b, const void *src) +{ + return dns_b_put(b, src, strlen(src)); +} + +DNS_NOTUSED static inline dns_error_t +dns_b_fmtju(struct dns_buf *b, const uintmax_t u, const unsigned width) +{ + size_t digits, padding, overflow; + uintmax_t r; + unsigned char *tp, *te, tc; + + digits = 0; + r = u; + do { + digits++; + r /= 10; + } while (r); + + padding = width - DNS_PP_MIN(digits, width); + overflow = (digits + padding) - DNS_PP_MIN((size_t)(b->pe - b->p), (digits + padding)); + + while (padding--) { + dns_b_putc(b, '0'); + } + + digits = 0; + tp = b->p; + r = u; + do { + if (overflow < ++digits) + dns_b_putc(b, '0' + (r % 10)); + r /= 10; + } while (r); + + te = b->p; + while (tp < te) { + tc = *--te; + *te = *tp; + *tp++ = tc; + } + + return b->error; +} + +static void +dns_b_popc(struct dns_buf *b) +{ + if (b->overflow && !--b->overflow) + b->error = 0; + if (b->p > b->base) + b->p--; +} + +static inline const char * +dns_b_tolstring(struct dns_buf *b, size_t *n) +{ + if (b->p < b->pe) { + *b->p = '\0'; + *n = b->p - b->base; + + return (const char *)b->base; + } else if (b->p > b->base) { + if (b->p[-1] != '\0') { + dns_b_setoverflow(b, 1, DNS_ENOBUFS); + b->p[-1] = '\0'; + } + *n = &b->p[-1] - b->base; + + return (const char *)b->base; + } else { + *n = 0; + + return ""; + } +} + +static inline const char * +dns_b_tostring(struct dns_buf *b) +{ + size_t n; + return dns_b_tolstring(b, &n); +} + +static inline size_t +dns_b_strlen(struct dns_buf *b) +{ + size_t n; + dns_b_tolstring(b, &n); + return n; +} + +static inline size_t +dns_b_strllen(struct dns_buf *b) +{ + size_t n = dns_b_strlen(b); + return n + b->overflow; +} + +DNS_NOTUSED static const struct dns_buf * +dns_b_from(const struct dns_buf *b, const void *src, size_t n) +{ + *(struct dns_buf *)b = (struct dns_buf)DNS_B_FROM(src, n); + + return b; +} + +static inline int +dns_b_getc(const struct dns_buf *_b, const int eof) +{ + struct dns_buf *b = (struct dns_buf *)_b; + + if (!(b->p < b->pe)) + return dns_b_setoverflow(b, 1, DNS_EILLEGAL), eof; + + return *b->p++; +} + +static inline intmax_t +dns_b_get16(const struct dns_buf *b, const intmax_t eof) +{ + intmax_t n; + + n = (dns_b_getc(b, 0) << 8); + n |= (dns_b_getc(b, 0) << 0); + + return (!b->overflow)? n : eof; +} + +DNS_NOTUSED static inline intmax_t +dns_b_get32(const struct dns_buf *b, const intmax_t eof) +{ + intmax_t n; + + n = (dns_b_get16(b, 0) << 16); + n |= (dns_b_get16(b, 0) << 0); + + return (!b->overflow)? n : eof; +} + +static inline dns_error_t +dns_b_move(struct dns_buf *dst, const struct dns_buf *_src, size_t n) +{ + struct dns_buf *src = (struct dns_buf *)_src; + size_t src_n = DNS_PP_MIN((size_t)(src->pe - src->p), n); + size_t src_r = n - src_n; + + dns_b_put(dst, src->p, src_n); + src->p += src_n; + + if (src_r) + return dns_b_setoverflow(src, src_r, DNS_EILLEGAL); + + return dst->error; +} + + +/* + * P A C K E T R O U T I N E S + * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +unsigned dns_p_count(struct dns_packet *P, enum dns_section section) { + unsigned count; + + switch (section) { + case DNS_S_QD: + return ntohs(dns_header(P)->qdcount); + case DNS_S_AN: + return ntohs(dns_header(P)->ancount); + case DNS_S_NS: + return ntohs(dns_header(P)->nscount); + case DNS_S_AR: + return ntohs(dns_header(P)->arcount); + default: + count = 0; + + if (section & DNS_S_QD) + count += ntohs(dns_header(P)->qdcount); + if (section & DNS_S_AN) + count += ntohs(dns_header(P)->ancount); + if (section & DNS_S_NS) + count += ntohs(dns_header(P)->nscount); + if (section & DNS_S_AR) + count += ntohs(dns_header(P)->arcount); + + return count; + } +} /* dns_p_count() */ + + +struct dns_packet *dns_p_init(struct dns_packet *P, size_t size) { + if (!P) + return 0; + + assert(size >= offsetof(struct dns_packet, data) + 12); + + memset(P, 0, sizeof *P); + P->size = size - offsetof(struct dns_packet, data); + P->end = 12; + + memset(P->data, '\0', 12); + + return P; +} /* dns_p_init() */ + + +static struct dns_packet *dns_p_reset(struct dns_packet *P) { + return dns_p_init(P, offsetof(struct dns_packet, data) + P->size); +} /* dns_p_reset() */ + + +static unsigned short dns_p_qend(struct dns_packet *P) { + unsigned short qend = 12; + unsigned i, count = dns_p_count(P, DNS_S_QD); + + for (i = 0; i < count && qend < P->end; i++) { + if (P->end == (qend = dns_d_skip(qend, P))) + goto invalid; + + if (P->end - qend < 4) + goto invalid; + + qend += 4; + } + + return DNS_PP_MIN(qend, P->end); +invalid: + return P->end; +} /* dns_p_qend() */ + + +struct dns_packet *dns_p_make(size_t len, int *error) { + struct dns_packet *P; + size_t size = dns_p_calcsize(len); + + if (!(P = dns_p_init(malloc(size), size))) + *error = dns_syerr(); + + return P; +} /* dns_p_make() */ + + +static void dns_p_free(struct dns_packet *P) { + free(P); +} /* dns_p_free() */ + + +/* convience routine to free any existing packet before storing new packet */ +static struct dns_packet *dns_p_setptr(struct dns_packet **dst, struct dns_packet *src) { + dns_p_free(*dst); + + *dst = src; + + return src; +} /* dns_p_setptr() */ + + +static struct dns_packet *dns_p_movptr(struct dns_packet **dst, struct dns_packet **src) { + dns_p_setptr(dst, *src); + + *src = NULL; + + return *dst; +} /* dns_p_movptr() */ + + +int dns_p_grow(struct dns_packet **P) { + struct dns_packet *tmp; + size_t size; + int error; + + if (!*P) { + if (!(*P = dns_p_make(DNS_P_QBUFSIZ, &error))) + return error; + + return 0; + } + + size = dns_p_sizeof(*P); + size |= size >> 1; + size |= size >> 2; + size |= size >> 4; + size |= size >> 8; + size++; + + if (size > 65536) + return DNS_ENOBUFS; + + if (!(tmp = realloc(*P, dns_p_calcsize(size)))) + return dns_syerr(); + + tmp->size = size; + *P = tmp; + + return 0; +} /* dns_p_grow() */ + + +struct dns_packet *dns_p_copy(struct dns_packet *P, const struct dns_packet *P0) { + if (!P) + return 0; + + P->end = DNS_PP_MIN(P->size, P0->end); + + memcpy(P->data, P0->data, P->end); + + return P; +} /* dns_p_copy() */ + + +struct dns_packet *dns_p_merge(struct dns_packet *A, enum dns_section Amask, struct dns_packet *B, enum dns_section Bmask, int *error_) { + size_t bufsiz = DNS_PP_MIN(65535, ((A)? A->end : 0) + ((B)? B->end : 0)); + struct dns_packet *M; + enum dns_section section; + struct dns_rr rr, mr; + int error, copy; + + if (!A && B) { + A = B; + Amask = Bmask; + B = 0; + } + +merge: + if (!(M = dns_p_make(bufsiz, &error))) + goto error; + + for (section = DNS_S_QD; (DNS_S_ALL & section); section <<= 1) { + if (A && (section & Amask)) { + dns_rr_foreach(&rr, A, .section = section) { + if ((error = dns_rr_copy(M, &rr, A))) + goto error; + } + } + + if (B && (section & Bmask)) { + dns_rr_foreach(&rr, B, .section = section) { + copy = 1; + + dns_rr_foreach(&mr, M, .type = rr.type, .section = DNS_S_ALL) { + if (!(copy = dns_rr_cmp(&rr, B, &mr, M))) + break; + } + + if (copy && (error = dns_rr_copy(M, &rr, B))) + goto error; + } + } + } + + return M; +error: + dns_p_setptr(&M, NULL); + + if (error == DNS_ENOBUFS && bufsiz < 65535) { + bufsiz = DNS_PP_MIN(65535, bufsiz * 2); + + goto merge; + } + + *error_ = error; + + return 0; +} /* dns_p_merge() */ + + +static unsigned short dns_l_skip(unsigned short, const unsigned char *, size_t); + +void dns_p_dictadd(struct dns_packet *P, unsigned short dn) { + unsigned short lp, lptr, i; + + lp = dn; + + while (lp < P->end) { + if (0xc0 == (0xc0 & P->data[lp]) && P->end - lp >= 2 && lp != dn) { + lptr = ((0x3f & P->data[lp + 0]) << 8) + | ((0xff & P->data[lp + 1]) << 0); + + for (i = 0; i < lengthof(P->dict) && P->dict[i]; i++) { + if (P->dict[i] == lptr) { + P->dict[i] = dn; + + return; + } + } + } + + lp = dns_l_skip(lp, P->data, P->end); + } + + for (i = 0; i < lengthof(P->dict); i++) { + if (!P->dict[i]) { + P->dict[i] = dn; + + break; + } + } +} /* dns_p_dictadd() */ + + +int dns_p_push(struct dns_packet *P, enum dns_section section, const void *dn, size_t dnlen, enum dns_type type, enum dns_class class, unsigned ttl, const void *any) { + size_t end = P->end; + int error; + + if ((error = dns_d_push(P, dn, dnlen))) + goto error; + + if (P->size - P->end < 4) + goto nobufs; + + P->data[P->end++] = 0xff & (type >> 8); + P->data[P->end++] = 0xff & (type >> 0); + + P->data[P->end++] = 0xff & (class >> 8); + P->data[P->end++] = 0xff & (class >> 0); + + if (section == DNS_S_QD) + goto update; + + if (P->size - P->end < 6) + goto nobufs; + + if (type != DNS_T_OPT) + ttl = DNS_PP_MIN(ttl, 0x7fffffffU); + P->data[P->end++] = ttl >> 24; + P->data[P->end++] = ttl >> 16; + P->data[P->end++] = ttl >> 8; + P->data[P->end++] = ttl >> 0; + + if ((error = dns_any_push(P, (union dns_any *)any, type))) + goto error; + +update: + switch (section) { + case DNS_S_QD: + if (dns_p_count(P, DNS_S_AN|DNS_S_NS|DNS_S_AR)) + goto order; + + if (!P->memo.qd.base && (error = dns_p_study(P))) + goto error; + + dns_header(P)->qdcount = htons(ntohs(dns_header(P)->qdcount) + 1); + + P->memo.qd.end = P->end; + P->memo.an.base = P->end; + P->memo.an.end = P->end; + P->memo.ns.base = P->end; + P->memo.ns.end = P->end; + P->memo.ar.base = P->end; + P->memo.ar.end = P->end; + + break; + case DNS_S_AN: + if (dns_p_count(P, DNS_S_NS|DNS_S_AR)) + goto order; + + if (!P->memo.an.base && (error = dns_p_study(P))) + goto error; + + dns_header(P)->ancount = htons(ntohs(dns_header(P)->ancount) + 1); + + P->memo.an.end = P->end; + P->memo.ns.base = P->end; + P->memo.ns.end = P->end; + P->memo.ar.base = P->end; + P->memo.ar.end = P->end; + + break; + case DNS_S_NS: + if (dns_p_count(P, DNS_S_AR)) + goto order; + + if (!P->memo.ns.base && (error = dns_p_study(P))) + goto error; + + dns_header(P)->nscount = htons(ntohs(dns_header(P)->nscount) + 1); + + P->memo.ns.end = P->end; + P->memo.ar.base = P->end; + P->memo.ar.end = P->end; + + break; + case DNS_S_AR: + if (!P->memo.ar.base && (error = dns_p_study(P))) + goto error; + + dns_header(P)->arcount = htons(ntohs(dns_header(P)->arcount) + 1); + + P->memo.ar.end = P->end; + + if (type == DNS_T_OPT && !P->memo.opt.p) { + P->memo.opt.p = end; + P->memo.opt.maxudp = class; + P->memo.opt.ttl = ttl; + } + + break; + default: + error = DNS_ESECTION; + + goto error; + } /* switch() */ + + return 0; +nobufs: + error = DNS_ENOBUFS; + + goto error; +order: + error = DNS_EORDER; + + goto error; +error: + P->end = end; + + return error; +} /* dns_p_push() */ + + +static void dns_p_dump3(struct dns_packet *P, struct dns_rr_i *I, FILE *fp) { + enum dns_section section; + struct dns_rr rr; + int error; + union dns_any any; + char pretty[sizeof any * 2]; + size_t len; + + fputs(";; [HEADER]\n", fp); + fprintf(fp, ";; qid : %d\n", ntohs(dns_header(P)->qid)); + fprintf(fp, ";; qr : %s(%d)\n", (dns_header(P)->qr)? "RESPONSE" : "QUERY", dns_header(P)->qr); + fprintf(fp, ";; opcode : %s(%d)\n", dns_stropcode(dns_header(P)->opcode), dns_header(P)->opcode); + fprintf(fp, ";; aa : %s(%d)\n", (dns_header(P)->aa)? "AUTHORITATIVE" : "NON-AUTHORITATIVE", dns_header(P)->aa); + fprintf(fp, ";; tc : %s(%d)\n", (dns_header(P)->tc)? "TRUNCATED" : "NOT-TRUNCATED", dns_header(P)->tc); + fprintf(fp, ";; rd : %s(%d)\n", (dns_header(P)->rd)? "RECURSION-DESIRED" : "RECURSION-NOT-DESIRED", dns_header(P)->rd); + fprintf(fp, ";; ra : %s(%d)\n", (dns_header(P)->ra)? "RECURSION-ALLOWED" : "RECURSION-NOT-ALLOWED", dns_header(P)->ra); + fprintf(fp, ";; rcode : %s(%d)\n", dns_strrcode(dns_p_rcode(P)), dns_p_rcode(P)); + + section = 0; + + while (dns_rr_grep(&rr, 1, I, P, &error)) { + if (section != rr.section) + fprintf(fp, "\n;; [%s:%d]\n", dns_strsection(rr.section), dns_p_count(P, rr.section)); + + if ((len = dns_rr_print(pretty, sizeof pretty, &rr, P, &error))) + fprintf(fp, "%s\n", pretty); + + section = rr.section; + } +} /* dns_p_dump3() */ + + +void dns_p_dump(struct dns_packet *P, FILE *fp) { + dns_p_dump3(P, dns_rr_i_new(P, .section = 0), fp); +} /* dns_p_dump() */ + + +static void dns_s_unstudy(struct dns_s_memo *m) + { m->base = 0; m->end = 0; } + +static void dns_m_unstudy(struct dns_p_memo *m) { + dns_s_unstudy(&m->qd); + dns_s_unstudy(&m->an); + dns_s_unstudy(&m->ns); + dns_s_unstudy(&m->ar); + m->opt.p = 0; + m->opt.maxudp = 0; + m->opt.ttl = 0; +} /* dns_m_unstudy() */ + +static int dns_s_study(struct dns_s_memo *m, enum dns_section section, unsigned short base, struct dns_packet *P) { + unsigned short count, rp; + + count = dns_p_count(P, section); + + for (rp = base; count && rp < P->end; count--) + rp = dns_rr_skip(rp, P); + + m->base = base; + m->end = rp; + + return 0; +} /* dns_s_study() */ + +static int dns_m_study(struct dns_p_memo *m, struct dns_packet *P) { + struct dns_rr rr; + int error; + + if ((error = dns_s_study(&m->qd, DNS_S_QD, 12, P))) + goto error; + if ((error = dns_s_study(&m->an, DNS_S_AN, m->qd.end, P))) + goto error; + if ((error = dns_s_study(&m->ns, DNS_S_NS, m->an.end, P))) + goto error; + if ((error = dns_s_study(&m->ar, DNS_S_AR, m->ns.end, P))) + goto error; + + m->opt.p = 0; + m->opt.maxudp = 0; + m->opt.ttl = 0; + dns_rr_foreach(&rr, P, .type = DNS_T_OPT, .section = DNS_S_AR) { + m->opt.p = rr.dn.p; + m->opt.maxudp = rr.class; + m->opt.ttl = rr.ttl; + break; + } + + return 0; +error: + dns_m_unstudy(m); + + return error; +} /* dns_m_study() */ + +int dns_p_study(struct dns_packet *P) { + return dns_m_study(&P->memo, P); +} /* dns_p_study() */ + + +enum dns_rcode dns_p_rcode(struct dns_packet *P) { + return 0xfff & ((P->memo.opt.ttl >> 20) | dns_header(P)->rcode); +} /* dns_p_rcode() */ + + +/* + * Q U E R Y P A C K E T R O U T I N E S + * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#define DNS_Q_RD 0x1 /* recursion desired */ +#define DNS_Q_EDNS0 0x2 /* include OPT RR */ + +static dns_error_t +dns_q_make2(struct dns_packet **_Q, const char *qname, size_t qlen, enum dns_type qtype, enum dns_class qclass, int qflags) +{ + struct dns_packet *Q = NULL; + int error; + + if (dns_p_movptr(&Q, _Q)) { + dns_p_reset(Q); + } else if (!(Q = dns_p_make(DNS_P_QBUFSIZ, &error))) { + goto error; + } + + if ((error = dns_p_push(Q, DNS_S_QD, qname, qlen, qtype, qclass, 0, 0))) + goto error; + + dns_header(Q)->rd = !!(qflags & DNS_Q_RD); + + if (qflags & DNS_Q_EDNS0) { + struct dns_opt opt = DNS_OPT_INIT(&opt); + + opt.version = 0; /* RFC 6891 version */ + opt.maxudp = 4096; + + if ((error = dns_p_push(Q, DNS_S_AR, ".", 1, DNS_T_OPT, dns_opt_class(&opt), dns_opt_ttl(&opt), &opt))) + goto error; + } + + *_Q = Q; + + return 0; +error: + dns_p_free(Q); + + return error; +} + +static dns_error_t +dns_q_make(struct dns_packet **Q, const char *qname, enum dns_type qtype, enum dns_class qclass, int qflags) +{ + return dns_q_make2(Q, qname, strlen(qname), qtype, qclass, qflags); +} + +static dns_error_t +dns_q_remake(struct dns_packet **Q, int qflags) +{ + char qname[DNS_D_MAXNAME + 1]; + size_t qlen; + struct dns_rr rr; + int error; + + assert(Q && *Q); + if ((error = dns_rr_parse(&rr, 12, *Q))) + return error; + if (!(qlen = dns_d_expand(qname, sizeof qname, rr.dn.p, *Q, &error))) + return error; + if (qlen >= sizeof qname) + return DNS_EILLEGAL; + return dns_q_make2(Q, qname, qlen, rr.type, rr.class, qflags); +} + +/* + * D O M A I N N A M E R O U T I N E S + * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#ifndef DNS_D_MAXPTRS +#define DNS_D_MAXPTRS 127 /* Arbitrary; possible, valid depth is something like packet size / 2 + fudge. */ +#endif + +static size_t dns_l_expand(unsigned char *dst, size_t lim, unsigned short src, unsigned short *nxt, const unsigned char *data, size_t end) { + unsigned short len; + unsigned nptrs = 0; + +retry: + if (src >= end) + goto invalid; + + switch (0x03 & (data[src] >> 6)) { + case 0x00: + len = (0x3f & (data[src++])); + + if (end - src < len) + goto invalid; + + if (lim > 0) { + memcpy(dst, &data[src], DNS_PP_MIN(lim, len)); + + dst[DNS_PP_MIN(lim - 1, len)] = '\0'; + } + + *nxt = src + len; + + return len; + case 0x01: + goto invalid; + case 0x02: + goto invalid; + case 0x03: + if (++nptrs > DNS_D_MAXPTRS) + goto invalid; + + if (end - src < 2) + goto invalid; + + src = ((0x3f & data[src + 0]) << 8) + | ((0xff & data[src + 1]) << 0); + + goto retry; + } /* switch() */ + + /* NOT REACHED */ +invalid: + *nxt = end; + + return 0; +} /* dns_l_expand() */ + + +static unsigned short dns_l_skip(unsigned short src, const unsigned char *data, size_t end) { + unsigned short len; + + if (src >= end) + goto invalid; + + switch (0x03 & (data[src] >> 6)) { + case 0x00: + len = (0x3f & (data[src++])); + + if (end - src < len) + goto invalid; + + return (len)? src + len : end; + case 0x01: + goto invalid; + case 0x02: + goto invalid; + case 0x03: + return end; + } /* switch() */ + + /* NOT REACHED */ +invalid: + return end; +} /* dns_l_skip() */ + + +static _Bool dns_d_isanchored(const void *_src, size_t len) { + const unsigned char *src = _src; + return len > 0 && src[len - 1] == '.'; +} /* dns_d_isanchored() */ + + +static size_t dns_d_ndots(const void *_src, size_t len) { + const unsigned char *p = _src, *pe = p + len; + size_t ndots = 0; + + while ((p = memchr(p, '.', pe - p))) { + ndots++; + p++; + } + + return ndots; +} /* dns_d_ndots() */ + + +static size_t dns_d_trim(void *dst_, size_t lim, const void *src_, size_t len, int flags) { + unsigned char *dst = dst_; + const unsigned char *src = src_; + size_t dp = 0, sp = 0; + int lc; + + /* trim any leading dot(s) */ + while (sp < len && src[sp] == '.') + sp++; + + for (lc = 0; sp < len; lc = src[sp++]) { + /* trim extra dot(s) */ + if (src[sp] == '.' && lc == '.') + continue; + + if (dp < lim) + dst[dp] = src[sp]; + + dp++; + } + + if ((flags & DNS_D_ANCHOR) && lc != '.') { + if (dp < lim) + dst[dp] = '.'; + + dp++; + } + + if (lim > 0) + dst[DNS_PP_MIN(dp, lim - 1)] = '\0'; + + return dp; +} /* dns_d_trim() */ + + +char *dns_d_init(void *dst, size_t lim, const void *src, size_t len, int flags) { + if (flags & DNS_D_TRIM) { + dns_d_trim(dst, lim, src, len, flags); + } if (flags & DNS_D_ANCHOR) { + dns_d_anchor(dst, lim, src, len); + } else { + memmove(dst, src, DNS_PP_MIN(lim, len)); + + if (lim > 0) + ((char *)dst)[DNS_PP_MIN(len, lim - 1)] = '\0'; + } + + return dst; +} /* dns_d_init() */ + + +size_t dns_d_anchor(void *dst, size_t lim, const void *src, size_t len) { + if (len == 0) + return 0; + + memmove(dst, src, DNS_PP_MIN(lim, len)); + + if (((const char *)src)[len - 1] != '.') { + if (len < lim) + ((char *)dst)[len] = '.'; + len++; + } + + if (lim > 0) + ((char *)dst)[DNS_PP_MIN(lim - 1, len)] = '\0'; + + return len; +} /* dns_d_anchor() */ + + +size_t dns_d_cleave(void *dst, size_t lim, const void *src, size_t len) { + const char *dot; + + /* XXX: Skip any leading dot. Handles cleaving root ".". */ + if (len == 0 || !(dot = memchr((const char *)src + 1, '.', len - 1))) + return 0; + + len -= dot - (const char *)src; + + /* XXX: Unless root, skip the label's trailing dot. */ + if (len > 1) { + src = ++dot; + len--; + } else + src = dot; + + memmove(dst, src, DNS_PP_MIN(lim, len)); + + if (lim > 0) + ((char *)dst)[DNS_PP_MIN(lim - 1, len)] = '\0'; + + return len; +} /* dns_d_cleave() */ + + +size_t dns_d_comp(void *dst_, size_t lim, const void *src_, size_t len, struct dns_packet *P, int *error) { + struct { unsigned char *b; size_t p, x; } dst, src; + unsigned char ch = '.'; + + dst.b = dst_; + dst.p = 0; + dst.x = 1; + + src.b = (unsigned char *)src_; + src.p = 0; + src.x = 0; + + while (src.x < len) { + ch = src.b[src.x]; + + if (ch == '.') { + if (dst.p < lim) + dst.b[dst.p] = (0x3f & (src.x - src.p)); + + dst.p = dst.x++; + src.p = ++src.x; + } else { + if (dst.x < lim) + dst.b[dst.x] = ch; + + dst.x++; + src.x++; + } + } /* while() */ + + if (src.x > src.p) { + if (dst.p < lim) + dst.b[dst.p] = (0x3f & (src.x - src.p)); + + dst.p = dst.x; + } + + if (dst.p > 1) { + if (dst.p < lim) + dst.b[dst.p] = 0x00; + + dst.p++; + } + +#if 1 + if (dst.p < lim) { + struct { unsigned char label[DNS_D_MAXLABEL + 1]; size_t len; unsigned short p, x, y; } a, b; + unsigned i; + + a.p = 0; + + while ((a.len = dns_l_expand(a.label, sizeof a.label, a.p, &a.x, dst.b, lim))) { + for (i = 0; i < lengthof(P->dict) && P->dict[i]; i++) { + b.p = P->dict[i]; + + while ((b.len = dns_l_expand(b.label, sizeof b.label, b.p, &b.x, P->data, P->end))) { + a.y = a.x; + b.y = b.x; + + while (a.len && b.len && 0 == strcasecmp((char *)a.label, (char *)b.label)) { + a.len = dns_l_expand(a.label, sizeof a.label, a.y, &a.y, dst.b, lim); + b.len = dns_l_expand(b.label, sizeof b.label, b.y, &b.y, P->data, P->end); + } + + if (a.len == 0 && b.len == 0 && b.p <= 0x3fff) { + dst.b[a.p++] = 0xc0 + | (0x3f & (b.p >> 8)); + dst.b[a.p++] = (0xff & (b.p >> 0)); + + /* silence static analyzers */ + dns_assume(a.p > 0); + + return a.p; + } + + b.p = b.x; + } /* while() */ + } /* for() */ + + a.p = a.x; + } /* while() */ + } /* if () */ +#endif + + if (!dst.p) + *error = DNS_EILLEGAL; + + return dst.p; +} /* dns_d_comp() */ + + +unsigned short dns_d_skip(unsigned short src, struct dns_packet *P) { + unsigned short len; + + while (src < P->end) { + switch (0x03 & (P->data[src] >> 6)) { + case 0x00: /* FOLLOWS */ + len = (0x3f & P->data[src++]); + + if (0 == len) { +/* success ==> */ return src; + } else if (P->end - src > len) { + src += len; + + break; + } else + goto invalid; + + /* NOT REACHED */ + case 0x01: /* RESERVED */ + goto invalid; + case 0x02: /* RESERVED */ + goto invalid; + case 0x03: /* POINTER */ + if (P->end - src < 2) + goto invalid; + + src += 2; + +/* success ==> */ return src; + } /* switch() */ + } /* while() */ + +invalid: + return P->end; +} /* dns_d_skip() */ + + +#include + +size_t dns_d_expand(void *dst, size_t lim, unsigned short src, struct dns_packet *P, int *error) { + size_t dstp = 0; + unsigned nptrs = 0; + unsigned char len; + + while (src < P->end) { + switch ((0x03 & (P->data[src] >> 6))) { + case 0x00: /* FOLLOWS */ + len = (0x3f & P->data[src]); + + if (0 == len) { + if (dstp == 0) { + if (dstp < lim) + ((unsigned char *)dst)[dstp] = '.'; + + dstp++; + } + + /* NUL terminate */ + if (lim > 0) + ((unsigned char *)dst)[DNS_PP_MIN(dstp, lim - 1)] = '\0'; + +/* success ==> */ return dstp; + } + + src++; + + if (P->end - src < len) + goto toolong; + + if (dstp < lim) + memcpy(&((unsigned char *)dst)[dstp], &P->data[src], DNS_PP_MIN(len, lim - dstp)); + + src += len; + dstp += len; + + if (dstp < lim) + ((unsigned char *)dst)[dstp] = '.'; + + dstp++; + + nptrs = 0; + + continue; + case 0x01: /* RESERVED */ + goto reserved; + case 0x02: /* RESERVED */ + goto reserved; + case 0x03: /* POINTER */ + if (++nptrs > DNS_D_MAXPTRS) + goto toolong; + + if (P->end - src < 2) + goto toolong; + + src = ((0x3f & P->data[src + 0]) << 8) + | ((0xff & P->data[src + 1]) << 0); + + continue; + } /* switch() */ + } /* while() */ + +toolong: + *error = DNS_EILLEGAL; + + if (lim > 0) + ((unsigned char *)dst)[DNS_PP_MIN(dstp, lim - 1)] = '\0'; + + return 0; +reserved: + *error = DNS_EILLEGAL; + + if (lim > 0) + ((unsigned char *)dst)[DNS_PP_MIN(dstp, lim - 1)] = '\0'; + + return 0; +} /* dns_d_expand() */ + + +int dns_d_push(struct dns_packet *P, const void *dn, size_t len) { + size_t lim = P->size - P->end; + unsigned dp = P->end; + int error = DNS_EILLEGAL; /* silence compiler */ + + len = dns_d_comp(&P->data[dp], lim, dn, len, P, &error); + + if (len == 0) + return error; + if (len > lim) + return DNS_ENOBUFS; + + P->end += len; + + dns_p_dictadd(P, dp); + + return 0; +} /* dns_d_push() */ + + +size_t dns_d_cname(void *dst, size_t lim, const void *dn, size_t len, struct dns_packet *P, int *error_) { + char host[DNS_D_MAXNAME + 1]; + struct dns_rr_i i; + struct dns_rr rr; + unsigned depth; + int error; + + if (sizeof host <= dns_d_anchor(host, sizeof host, dn, len)) + { error = ENAMETOOLONG; goto error; } + + for (depth = 0; depth < 7; depth++) { + dns_rr_i_init(memset(&i, 0, sizeof i), P); + + i.section = DNS_S_ALL & ~DNS_S_QD; + i.name = host; + i.type = DNS_T_CNAME; + + if (!dns_rr_grep(&rr, 1, &i, P, &error)) + break; + + if ((error = dns_cname_parse((struct dns_cname *)host, &rr, P))) + goto error; + } + + return dns_strlcpy(dst, host, lim); +error: + *error_ = error; + + return 0; +} /* dns_d_cname() */ + + +/* + * R E S O U R C E R E C O R D R O U T I N E S + * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +int dns_rr_copy(struct dns_packet *P, struct dns_rr *rr, struct dns_packet *Q) { + unsigned char dn[DNS_D_MAXNAME + 1]; + union dns_any any; + size_t len; + int error; + + if (!(len = dns_d_expand(dn, sizeof dn, rr->dn.p, Q, &error))) + return error; + else if (len >= sizeof dn) + return DNS_EILLEGAL; + + if (rr->section != DNS_S_QD && (error = dns_any_parse(dns_any_init(&any, sizeof any), rr, Q))) + return error; + + return dns_p_push(P, rr->section, dn, len, rr->type, rr->class, rr->ttl, &any); +} /* dns_rr_copy() */ + + +int dns_rr_parse(struct dns_rr *rr, unsigned short src, struct dns_packet *P) { + unsigned short p = src; + + if (src >= P->end) + goto invalid; + + rr->dn.p = p; + rr->dn.len = (p = dns_d_skip(p, P)) - rr->dn.p; + + if (P->end - p < 4) + goto invalid; + + rr->type = ((0xff & P->data[p + 0]) << 8) + | ((0xff & P->data[p + 1]) << 0); + + rr->class = ((0xff & P->data[p + 2]) << 8) + | ((0xff & P->data[p + 3]) << 0); + + p += 4; + + if (src < dns_p_qend(P)) { + rr->section = DNS_S_QUESTION; + + rr->ttl = 0; + rr->rd.p = 0; + rr->rd.len = 0; + + return 0; + } + + if (P->end - p < 4) + goto invalid; + + rr->ttl = ((0xff & P->data[p + 0]) << 24) + | ((0xff & P->data[p + 1]) << 16) + | ((0xff & P->data[p + 2]) << 8) + | ((0xff & P->data[p + 3]) << 0); + if (rr->type != DNS_T_OPT) + rr->ttl = DNS_PP_MIN(rr->ttl, 0x7fffffffU); + + p += 4; + + if (P->end - p < 2) + goto invalid; + + rr->rd.len = ((0xff & P->data[p + 0]) << 8) + | ((0xff & P->data[p + 1]) << 0); + rr->rd.p = p + 2; + + p += 2; + + if (P->end - p < rr->rd.len) + goto invalid; + + return 0; +invalid: + return DNS_EILLEGAL; +} /* dns_rr_parse() */ + + +static unsigned short dns_rr_len(const unsigned short src, struct dns_packet *P) { + unsigned short rp, rdlen; + + rp = dns_d_skip(src, P); + + if (P->end - rp < 4) + return P->end - src; + + rp += 4; /* TYPE, CLASS */ + + if (rp <= dns_p_qend(P)) + return rp - src; + + if (P->end - rp < 6) + return P->end - src; + + rp += 6; /* TTL, RDLEN */ + + rdlen = ((0xff & P->data[rp - 2]) << 8) + | ((0xff & P->data[rp - 1]) << 0); + + if (P->end - rp < rdlen) + return P->end - src; + + rp += rdlen; + + return rp - src; +} /* dns_rr_len() */ + + +unsigned short dns_rr_skip(unsigned short src, struct dns_packet *P) { + return src + dns_rr_len(src, P); +} /* dns_rr_skip() */ + + +static enum dns_section dns_rr_section(unsigned short src, struct dns_packet *P) { + enum dns_section section; + unsigned count, index; + unsigned short rp; + + if (src >= P->memo.qd.base && src < P->memo.qd.end) + return DNS_S_QD; + if (src >= P->memo.an.base && src < P->memo.an.end) + return DNS_S_AN; + if (src >= P->memo.ns.base && src < P->memo.ns.end) + return DNS_S_NS; + if (src >= P->memo.ar.base && src < P->memo.ar.end) + return DNS_S_AR; + + /* NOTE: Possibly bad memoization. Try it the hard-way. */ + + for (rp = 12, index = 0; rp < src && rp < P->end; index++) + rp = dns_rr_skip(rp, P); + + section = DNS_S_QD; + count = dns_p_count(P, section); + + while (index >= count && section <= DNS_S_AR) { + section <<= 1; + count += dns_p_count(P, section); + } + + return DNS_S_ALL & section; +} /* dns_rr_section() */ + + +static enum dns_type dns_rr_type(unsigned short src, struct dns_packet *P) { + struct dns_rr rr; + int error; + + if ((error = dns_rr_parse(&rr, src, P))) + return 0; + + return rr.type; +} /* dns_rr_type() */ + + +int dns_rr_cmp(struct dns_rr *r0, struct dns_packet *P0, struct dns_rr *r1, struct dns_packet *P1) { + char host0[DNS_D_MAXNAME + 1], host1[DNS_D_MAXNAME + 1]; + union dns_any any0, any1; + int cmp, error; + size_t len; + + if ((cmp = r0->type - r1->type)) + return cmp; + + if ((cmp = r0->class - r1->class)) + return cmp; + + /* + * FIXME: Do label-by-label comparison to handle illegally long names? + */ + + if (!(len = dns_d_expand(host0, sizeof host0, r0->dn.p, P0, &error)) + || len >= sizeof host0) + return -1; + + if (!(len = dns_d_expand(host1, sizeof host1, r1->dn.p, P1, &error)) + || len >= sizeof host1) + return 1; + + if ((cmp = strcasecmp(host0, host1))) + return cmp; + + if (DNS_S_QD & (r0->section | r1->section)) { + if (r0->section == r1->section) + return 0; + + return (r0->section == DNS_S_QD)? -1 : 1; + } + + if ((error = dns_any_parse(&any0, r0, P0))) + return -1; + + if ((error = dns_any_parse(&any1, r1, P1))) + return 1; + + return dns_any_cmp(&any0, r0->type, &any1, r1->type); +} /* dns_rr_cmp() */ + + +static _Bool dns_rr_exists(struct dns_rr *rr0, struct dns_packet *P0, struct dns_packet *P1) { + struct dns_rr rr1; + + dns_rr_foreach(&rr1, P1, .section = rr0->section, .type = rr0->type) { + if (0 == dns_rr_cmp(rr0, P0, &rr1, P1)) + return 1; + } + + return 0; +} /* dns_rr_exists() */ + + +static unsigned short dns_rr_offset(struct dns_rr *rr) { + return rr->dn.p; +} /* dns_rr_offset() */ + + +static _Bool dns_rr_i_match(struct dns_rr *rr, struct dns_rr_i *i, struct dns_packet *P) { + if (i->section && !(rr->section & i->section)) + return 0; + + if (i->type && rr->type != i->type && i->type != DNS_T_ALL) + return 0; + + if (i->class && rr->class != i->class && i->class != DNS_C_ANY) + return 0; + + if (i->name) { + char dn[DNS_D_MAXNAME + 1]; + size_t len; + int error; + + if (!(len = dns_d_expand(dn, sizeof dn, rr->dn.p, P, &error)) + || len >= sizeof dn) + return 0; + + if (0 != strcasecmp(dn, i->name)) + return 0; + } + + if (i->data && i->type && rr->section > DNS_S_QD) { + union dns_any rd; + int error; + + if ((error = dns_any_parse(&rd, rr, P))) + return 0; + + if (0 != dns_any_cmp(&rd, rr->type, i->data, i->type)) + return 0; + } + + return 1; +} /* dns_rr_i_match() */ + + +static unsigned short dns_rr_i_start(struct dns_rr_i *i, struct dns_packet *P) { + unsigned short rp; + struct dns_rr r0, rr; + int error; + + if ((i->section & DNS_S_QD) && P->memo.qd.base) + rp = P->memo.qd.base; + else if ((i->section & DNS_S_AN) && P->memo.an.base) + rp = P->memo.an.base; + else if ((i->section & DNS_S_NS) && P->memo.ns.base) + rp = P->memo.ns.base; + else if ((i->section & DNS_S_AR) && P->memo.ar.base) + rp = P->memo.ar.base; + else + rp = 12; + + for (; rp < P->end; rp = dns_rr_skip(rp, P)) { + if ((error = dns_rr_parse(&rr, rp, P))) + continue; + + rr.section = dns_rr_section(rp, P); + + if (!dns_rr_i_match(&rr, i, P)) + continue; + + r0 = rr; + + goto lower; + } + + return P->end; +lower: + if (i->sort == &dns_rr_i_packet) + return dns_rr_offset(&r0); + + while ((rp = dns_rr_skip(rp, P)) < P->end) { + if ((error = dns_rr_parse(&rr, rp, P))) + continue; + + rr.section = dns_rr_section(rp, P); + + if (!dns_rr_i_match(&rr, i, P)) + continue; + + if (i->sort(&rr, &r0, i, P) < 0) + r0 = rr; + } + + return dns_rr_offset(&r0); +} /* dns_rr_i_start() */ + + +static unsigned short dns_rr_i_skip(unsigned short rp, struct dns_rr_i *i, struct dns_packet *P) { + struct dns_rr r0, r1, rr; + int error; + + if ((error = dns_rr_parse(&r0, rp, P))) + return P->end; + + r0.section = dns_rr_section(rp, P); + + rp = (i->sort == &dns_rr_i_packet)? dns_rr_skip(rp, P) : 12; + + for (; rp < P->end; rp = dns_rr_skip(rp, P)) { + if ((error = dns_rr_parse(&rr, rp, P))) + continue; + + rr.section = dns_rr_section(rp, P); + + if (!dns_rr_i_match(&rr, i, P)) + continue; + + if (i->sort(&rr, &r0, i, P) <= 0) + continue; + + r1 = rr; + + goto lower; + } + + return P->end; +lower: + if (i->sort == &dns_rr_i_packet) + return dns_rr_offset(&r1); + + while ((rp = dns_rr_skip(rp, P)) < P->end) { + if ((error = dns_rr_parse(&rr, rp, P))) + continue; + + rr.section = dns_rr_section(rp, P); + + if (!dns_rr_i_match(&rr, i, P)) + continue; + + if (i->sort(&rr, &r0, i, P) <= 0) + continue; + + if (i->sort(&rr, &r1, i, P) >= 0) + continue; + + r1 = rr; + } + + return dns_rr_offset(&r1); +} /* dns_rr_i_skip() */ + + +int dns_rr_i_packet(struct dns_rr *a, struct dns_rr *b, struct dns_rr_i *i, struct dns_packet *P) { + (void)i; + (void)P; + + return (int)a->dn.p - (int)b->dn.p; +} /* dns_rr_i_packet() */ + + +int dns_rr_i_order(struct dns_rr *a, struct dns_rr *b, struct dns_rr_i *i, struct dns_packet *P) { + int cmp; + + (void)i; + + if ((cmp = a->section - b->section)) + return cmp; + + if (a->type != b->type) + return (int)a->dn.p - (int)b->dn.p; + + return dns_rr_cmp(a, P, b, P); +} /* dns_rr_i_order() */ + + +int dns_rr_i_shuffle(struct dns_rr *a, struct dns_rr *b, struct dns_rr_i *i, struct dns_packet *P) { + int cmp; + + (void)i; + (void)P; + + while (!i->state.regs[0]) + i->state.regs[0] = dns_random(); + + if ((cmp = a->section - b->section)) + return cmp; + + return dns_k_shuffle16(a->dn.p, i->state.regs[0]) - dns_k_shuffle16(b->dn.p, i->state.regs[0]); +} /* dns_rr_i_shuffle() */ + + +struct dns_rr_i *dns_rr_i_init(struct dns_rr_i *i, struct dns_packet *P) { + static const struct dns_rr_i i_initializer; + + (void)P; + + i->state = i_initializer.state; + i->saved = i->state; + + return i; +} /* dns_rr_i_init() */ + + +unsigned dns_rr_grep(struct dns_rr *rr, unsigned lim, struct dns_rr_i *i, struct dns_packet *P, int *error_) { + unsigned count = 0; + int error; + + switch (i->state.exec) { + case 0: + if (!i->sort) + i->sort = &dns_rr_i_packet; + + i->state.next = dns_rr_i_start(i, P); + i->state.exec++; + + /* FALL THROUGH */ + case 1: + while (count < lim && i->state.next < P->end) { + if ((error = dns_rr_parse(rr, i->state.next, P))) + goto error; + + rr->section = dns_rr_section(i->state.next, P); + + rr++; + count++; + i->state.count++; + + i->state.next = dns_rr_i_skip(i->state.next, i, P); + } /* while() */ + + break; + } /* switch() */ + + return count; +error: + *error_ = error; + + return count; +} /* dns_rr_grep() */ + + +size_t dns_rr_print(void *_dst, size_t lim, struct dns_rr *rr, struct dns_packet *P, int *_error) { + struct dns_buf dst = DNS_B_INTO(_dst, lim); + union dns_any any; + size_t n; + int error; + + if (rr->section == DNS_S_QD) + dns_b_putc(&dst, ';'); + + if (!(n = dns_d_expand(any.ns.host, sizeof any.ns.host, rr->dn.p, P, &error))) + goto error; + dns_b_put(&dst, any.ns.host, DNS_PP_MIN(n, sizeof any.ns.host - 1)); + + if (rr->section != DNS_S_QD) { + dns_b_putc(&dst, ' '); + dns_b_fmtju(&dst, rr->ttl, 0); + } + + dns_b_putc(&dst, ' '); + dns_b_puts(&dst, dns_strclass(rr->class)); + dns_b_putc(&dst, ' '); + dns_b_puts(&dst, dns_strtype(rr->type)); + + if (rr->section == DNS_S_QD) + goto epilog; + + dns_b_putc(&dst, ' '); + + if ((error = dns_any_parse(dns_any_init(&any, sizeof any), rr, P))) + goto error; + + n = dns_any_print(dst.p, dst.pe - dst.p, &any, rr->type); + dst.p += DNS_PP_MIN(n, (size_t)(dst.pe - dst.p)); +epilog: + return dns_b_strllen(&dst); +error: + *_error = error; + + return 0; +} /* dns_rr_print() */ + + +int dns_a_parse(struct dns_a *a, struct dns_rr *rr, struct dns_packet *P) { + unsigned long addr; + + if (rr->rd.len != 4) + return DNS_EILLEGAL; + + addr = ((0xffU & P->data[rr->rd.p + 0]) << 24) + | ((0xffU & P->data[rr->rd.p + 1]) << 16) + | ((0xffU & P->data[rr->rd.p + 2]) << 8) + | ((0xffU & P->data[rr->rd.p + 3]) << 0); + + a->addr.s_addr = htonl(addr); + + return 0; +} /* dns_a_parse() */ + + +int dns_a_push(struct dns_packet *P, struct dns_a *a) { + unsigned long addr; + + if (P->size - P->end < 6) + return DNS_ENOBUFS; + + P->data[P->end++] = 0x00; + P->data[P->end++] = 0x04; + + addr = ntohl(a->addr.s_addr); + + P->data[P->end++] = 0xffU & (addr >> 24); + P->data[P->end++] = 0xffU & (addr >> 16); + P->data[P->end++] = 0xffU & (addr >> 8); + P->data[P->end++] = 0xffU & (addr >> 0); + + return 0; +} /* dns_a_push() */ + + +size_t dns_a_arpa(void *_dst, size_t lim, const struct dns_a *a) { + struct dns_buf dst = DNS_B_INTO(_dst, lim); + unsigned long octets = ntohl(a->addr.s_addr); + unsigned i; + + for (i = 0; i < 4; i++) { + dns_b_fmtju(&dst, 0xff & octets, 0); + dns_b_putc(&dst, '.'); + octets >>= 8; + } + + dns_b_puts(&dst, "in-addr.arpa."); + + return dns_b_strllen(&dst); +} /* dns_a_arpa() */ + + +int dns_a_cmp(const struct dns_a *a, const struct dns_a *b) { + if (ntohl(a->addr.s_addr) < ntohl(b->addr.s_addr)) + return -1; + if (ntohl(a->addr.s_addr) > ntohl(b->addr.s_addr)) + return 1; + + return 0; +} /* dns_a_cmp() */ + + +size_t dns_a_print(void *dst, size_t lim, struct dns_a *a) { + char addr[INET_ADDRSTRLEN + 1] = "0.0.0.0"; + + dns_inet_ntop(AF_INET, &a->addr, addr, sizeof addr); + + return dns_strlcpy(dst, addr, lim); +} /* dns_a_print() */ + + +int dns_aaaa_parse(struct dns_aaaa *aaaa, struct dns_rr *rr, struct dns_packet *P) { + if (rr->rd.len != sizeof aaaa->addr.s6_addr) + return DNS_EILLEGAL; + + memcpy(aaaa->addr.s6_addr, &P->data[rr->rd.p], sizeof aaaa->addr.s6_addr); + + return 0; +} /* dns_aaaa_parse() */ + + +int dns_aaaa_push(struct dns_packet *P, struct dns_aaaa *aaaa) { + if (P->size - P->end < 2 + sizeof aaaa->addr.s6_addr) + return DNS_ENOBUFS; + + P->data[P->end++] = 0x00; + P->data[P->end++] = 0x10; + + memcpy(&P->data[P->end], aaaa->addr.s6_addr, sizeof aaaa->addr.s6_addr); + + P->end += sizeof aaaa->addr.s6_addr; + + return 0; +} /* dns_aaaa_push() */ + + +int dns_aaaa_cmp(const struct dns_aaaa *a, const struct dns_aaaa *b) { + unsigned i; + int cmp; + + for (i = 0; i < lengthof(a->addr.s6_addr); i++) { + if ((cmp = (a->addr.s6_addr[i] - b->addr.s6_addr[i]))) + return cmp; + } + + return 0; +} /* dns_aaaa_cmp() */ + + +size_t dns_aaaa_arpa(void *_dst, size_t lim, const struct dns_aaaa *aaaa) { + static const unsigned char hex[16] = "0123456789abcdef"; + struct dns_buf dst = DNS_B_INTO(_dst, lim); + unsigned nyble; + int i, j; + + for (i = sizeof aaaa->addr.s6_addr - 1; i >= 0; i--) { + nyble = aaaa->addr.s6_addr[i]; + + for (j = 0; j < 2; j++) { + dns_b_putc(&dst, hex[0x0f & nyble]); + dns_b_putc(&dst, '.'); + nyble >>= 4; + } + } + + dns_b_puts(&dst, "ip6.arpa."); + + return dns_b_strllen(&dst); +} /* dns_aaaa_arpa() */ + + +size_t dns_aaaa_print(void *dst, size_t lim, struct dns_aaaa *aaaa) { + char addr[INET6_ADDRSTRLEN + 1] = "::"; + + dns_inet_ntop(AF_INET6, &aaaa->addr, addr, sizeof addr); + + return dns_strlcpy(dst, addr, lim); +} /* dns_aaaa_print() */ + + +int dns_mx_parse(struct dns_mx *mx, struct dns_rr *rr, struct dns_packet *P) { + size_t len; + int error; + + if (rr->rd.len < 3) + return DNS_EILLEGAL; + + mx->preference = (0xff00 & (P->data[rr->rd.p + 0] << 8)) + | (0x00ff & (P->data[rr->rd.p + 1] << 0)); + + if (!(len = dns_d_expand(mx->host, sizeof mx->host, rr->rd.p + 2, P, &error))) + return error; + else if (len >= sizeof mx->host) + return DNS_EILLEGAL; + + return 0; +} /* dns_mx_parse() */ + + +int dns_mx_push(struct dns_packet *P, struct dns_mx *mx) { + size_t end, len; + int error; + + if (P->size - P->end < 5) + return DNS_ENOBUFS; + + end = P->end; + P->end += 2; + + P->data[P->end++] = 0xff & (mx->preference >> 8); + P->data[P->end++] = 0xff & (mx->preference >> 0); + + if ((error = dns_d_push(P, mx->host, strlen(mx->host)))) + goto error; + + len = P->end - end - 2; + + P->data[end + 0] = 0xff & (len >> 8); + P->data[end + 1] = 0xff & (len >> 0); + + return 0; +error: + P->end = end; + + return error; +} /* dns_mx_push() */ + + +int dns_mx_cmp(const struct dns_mx *a, const struct dns_mx *b) { + int cmp; + + if ((cmp = a->preference - b->preference)) + return cmp; + + return strcasecmp(a->host, b->host); +} /* dns_mx_cmp() */ + + +size_t dns_mx_print(void *_dst, size_t lim, struct dns_mx *mx) { + struct dns_buf dst = DNS_B_INTO(_dst, lim); + + dns_b_fmtju(&dst, mx->preference, 0); + dns_b_putc(&dst, ' '); + dns_b_puts(&dst, mx->host); + + return dns_b_strllen(&dst); +} /* dns_mx_print() */ + + +size_t dns_mx_cname(void *dst, size_t lim, struct dns_mx *mx) { + return dns_strlcpy(dst, mx->host, lim); +} /* dns_mx_cname() */ + + +int dns_ns_parse(struct dns_ns *ns, struct dns_rr *rr, struct dns_packet *P) { + size_t len; + int error; + + if (!(len = dns_d_expand(ns->host, sizeof ns->host, rr->rd.p, P, &error))) + return error; + else if (len >= sizeof ns->host) + return DNS_EILLEGAL; + + return 0; +} /* dns_ns_parse() */ + + +int dns_ns_push(struct dns_packet *P, struct dns_ns *ns) { + size_t end, len; + int error; + + if (P->size - P->end < 3) + return DNS_ENOBUFS; + + end = P->end; + P->end += 2; + + if ((error = dns_d_push(P, ns->host, strlen(ns->host)))) + goto error; + + len = P->end - end - 2; + + P->data[end + 0] = 0xff & (len >> 8); + P->data[end + 1] = 0xff & (len >> 0); + + return 0; +error: + P->end = end; + + return error; +} /* dns_ns_push() */ + + +int dns_ns_cmp(const struct dns_ns *a, const struct dns_ns *b) { + return strcasecmp(a->host, b->host); +} /* dns_ns_cmp() */ + + +size_t dns_ns_print(void *dst, size_t lim, struct dns_ns *ns) { + return dns_strlcpy(dst, ns->host, lim); +} /* dns_ns_print() */ + + +size_t dns_ns_cname(void *dst, size_t lim, struct dns_ns *ns) { + return dns_strlcpy(dst, ns->host, lim); +} /* dns_ns_cname() */ + + +int dns_cname_parse(struct dns_cname *cname, struct dns_rr *rr, struct dns_packet *P) { + return dns_ns_parse((struct dns_ns *)cname, rr, P); +} /* dns_cname_parse() */ + + +int dns_cname_push(struct dns_packet *P, struct dns_cname *cname) { + return dns_ns_push(P, (struct dns_ns *)cname); +} /* dns_cname_push() */ + + +int dns_cname_cmp(const struct dns_cname *a, const struct dns_cname *b) { + return strcasecmp(a->host, b->host); +} /* dns_cname_cmp() */ + + +size_t dns_cname_print(void *dst, size_t lim, struct dns_cname *cname) { + return dns_ns_print(dst, lim, (struct dns_ns *)cname); +} /* dns_cname_print() */ + + +size_t dns_cname_cname(void *dst, size_t lim, struct dns_cname *cname) { + return dns_strlcpy(dst, cname->host, lim); +} /* dns_cname_cname() */ + + +int dns_soa_parse(struct dns_soa *soa, struct dns_rr *rr, struct dns_packet *P) { + struct { void *dst; size_t lim; } dn[] = + { { soa->mname, sizeof soa->mname }, + { soa->rname, sizeof soa->rname } }; + unsigned *ts[] = + { &soa->serial, &soa->refresh, &soa->retry, &soa->expire, &soa->minimum }; + unsigned short rp; + unsigned i, j, n; + int error; + + /* MNAME / RNAME */ + if ((rp = rr->rd.p) >= P->end) + return DNS_EILLEGAL; + + for (i = 0; i < lengthof(dn); i++) { + if (!(n = dns_d_expand(dn[i].dst, dn[i].lim, rp, P, &error))) + return error; + else if (n >= dn[i].lim) + return DNS_EILLEGAL; + + if ((rp = dns_d_skip(rp, P)) >= P->end) + return DNS_EILLEGAL; + } + + /* SERIAL / REFRESH / RETRY / EXPIRE / MINIMUM */ + for (i = 0; i < lengthof(ts); i++) { + for (j = 0; j < 4; j++, rp++) { + if (rp >= P->end) + return DNS_EILLEGAL; + + *ts[i] <<= 8; + *ts[i] |= (0xff & P->data[rp]); + } + } + + return 0; +} /* dns_soa_parse() */ + + +int dns_soa_push(struct dns_packet *P, struct dns_soa *soa) { + void *dn[] = { soa->mname, soa->rname }; + unsigned ts[] = { (0xffffffff & soa->serial), + (0x7fffffff & soa->refresh), + (0x7fffffff & soa->retry), + (0x7fffffff & soa->expire), + (0xffffffff & soa->minimum) }; + unsigned i, j; + size_t end, len; + int error; + + end = P->end; + + if ((P->end += 2) >= P->size) + goto toolong; + + /* MNAME / RNAME */ + for (i = 0; i < lengthof(dn); i++) { + if ((error = dns_d_push(P, dn[i], strlen(dn[i])))) + goto error; + } + + /* SERIAL / REFRESH / RETRY / EXPIRE / MINIMUM */ + for (i = 0; i < lengthof(ts); i++) { + if ((P->end += 4) >= P->size) + goto toolong; + + for (j = 1; j <= 4; j++) { + P->data[P->end - j] = (0xff & ts[i]); + ts[i] >>= 8; + } + } + + len = P->end - end - 2; + P->data[end + 0] = (0xff & (len >> 8)); + P->data[end + 1] = (0xff & (len >> 0)); + + return 0; +toolong: + error = DNS_ENOBUFS; + + /* FALL THROUGH */ +error: + P->end = end; + + return error; +} /* dns_soa_push() */ + + +int dns_soa_cmp(const struct dns_soa *a, const struct dns_soa *b) { + int cmp; + + if ((cmp = strcasecmp(a->mname, b->mname))) + return cmp; + + if ((cmp = strcasecmp(a->rname, b->rname))) + return cmp; + + if (a->serial > b->serial) + return -1; + else if (a->serial < b->serial) + return 1; + + if (a->refresh > b->refresh) + return -1; + else if (a->refresh < b->refresh) + return 1; + + if (a->retry > b->retry) + return -1; + else if (a->retry < b->retry) + return 1; + + if (a->expire > b->expire) + return -1; + else if (a->expire < b->expire) + return 1; + + if (a->minimum > b->minimum) + return -1; + else if (a->minimum < b->minimum) + return 1; + + return 0; +} /* dns_soa_cmp() */ + + +size_t dns_soa_print(void *_dst, size_t lim, struct dns_soa *soa) { + struct dns_buf dst = DNS_B_INTO(_dst, lim); + + dns_b_puts(&dst, soa->mname); + dns_b_putc(&dst, ' '); + dns_b_puts(&dst, soa->rname); + dns_b_putc(&dst, ' '); + dns_b_fmtju(&dst, soa->serial, 0); + dns_b_putc(&dst, ' '); + dns_b_fmtju(&dst, soa->refresh, 0); + dns_b_putc(&dst, ' '); + dns_b_fmtju(&dst, soa->retry, 0); + dns_b_putc(&dst, ' '); + dns_b_fmtju(&dst, soa->expire, 0); + dns_b_putc(&dst, ' '); + dns_b_fmtju(&dst, soa->minimum, 0); + + return dns_b_strllen(&dst); +} /* dns_soa_print() */ + + +int dns_srv_parse(struct dns_srv *srv, struct dns_rr *rr, struct dns_packet *P) { + unsigned short rp; + unsigned i; + size_t n; + int error; + + memset(srv, '\0', sizeof *srv); + + rp = rr->rd.p; + + if (rr->rd.len < 7) + return DNS_EILLEGAL; + + for (i = 0; i < 2; i++, rp++) { + srv->priority <<= 8; + srv->priority |= (0xff & P->data[rp]); + } + + for (i = 0; i < 2; i++, rp++) { + srv->weight <<= 8; + srv->weight |= (0xff & P->data[rp]); + } + + for (i = 0; i < 2; i++, rp++) { + srv->port <<= 8; + srv->port |= (0xff & P->data[rp]); + } + + if (!(n = dns_d_expand(srv->target, sizeof srv->target, rp, P, &error))) + return error; + else if (n >= sizeof srv->target) + return DNS_EILLEGAL; + + return 0; +} /* dns_srv_parse() */ + + +int dns_srv_push(struct dns_packet *P, struct dns_srv *srv) { + size_t end, len; + int error; + + end = P->end; + + if (P->size - P->end < 2) + goto toolong; + + P->end += 2; + + if (P->size - P->end < 6) + goto toolong; + + P->data[P->end++] = 0xff & (srv->priority >> 8); + P->data[P->end++] = 0xff & (srv->priority >> 0); + + P->data[P->end++] = 0xff & (srv->weight >> 8); + P->data[P->end++] = 0xff & (srv->weight >> 0); + + P->data[P->end++] = 0xff & (srv->port >> 8); + P->data[P->end++] = 0xff & (srv->port >> 0); + + if (0 == (len = dns_d_comp(&P->data[P->end], P->size - P->end, srv->target, strlen(srv->target), P, &error))) + goto error; + else if (P->size - P->end < len) + goto toolong; + + P->end += len; + + if (P->end > 65535) + goto toolong; + + len = P->end - end - 2; + + P->data[end + 0] = 0xff & (len >> 8); + P->data[end + 1] = 0xff & (len >> 0); + + return 0; +toolong: + error = DNS_ENOBUFS; + + /* FALL THROUGH */ +error: + P->end = end; + + return error; +} /* dns_srv_push() */ + + +int dns_srv_cmp(const struct dns_srv *a, const struct dns_srv *b) { + int cmp; + + if ((cmp = a->priority - b->priority)) + return cmp; + + /* + * FIXME: We need some sort of random seed to implement the dynamic + * weighting required by RFC 2782. + */ + if ((cmp = a->weight - b->weight)) + return cmp; + + if ((cmp = a->port - b->port)) + return cmp; + + return strcasecmp(a->target, b->target); +} /* dns_srv_cmp() */ + + +size_t dns_srv_print(void *_dst, size_t lim, struct dns_srv *srv) { + struct dns_buf dst = DNS_B_INTO(_dst, lim); + + dns_b_fmtju(&dst, srv->priority, 0); + dns_b_putc(&dst, ' '); + dns_b_fmtju(&dst, srv->weight, 0); + dns_b_putc(&dst, ' '); + dns_b_fmtju(&dst, srv->port, 0); + dns_b_putc(&dst, ' '); + dns_b_puts(&dst, srv->target); + + return dns_b_strllen(&dst); +} /* dns_srv_print() */ + + +size_t dns_srv_cname(void *dst, size_t lim, struct dns_srv *srv) { + return dns_strlcpy(dst, srv->target, lim); +} /* dns_srv_cname() */ + + +unsigned int dns_opt_ttl(const struct dns_opt *opt) { + unsigned int ttl = 0; + + ttl |= (0xffU & opt->rcode) << 24; + ttl |= (0xffU & opt->version) << 16; + ttl |= (0xffffU & opt->flags) << 0; + + return ttl; +} /* dns_opt_ttl() */ + + +unsigned short dns_opt_class(const struct dns_opt *opt) { + return opt->maxudp; +} /* dns_opt_class() */ + + +struct dns_opt *dns_opt_init(struct dns_opt *opt, size_t size) { + assert(size >= offsetof(struct dns_opt, data)); + + opt->size = size - offsetof(struct dns_opt, data); + opt->len = 0; + + opt->rcode = 0; + opt->version = 0; + opt->maxudp = 0; + + return opt; +} /* dns_opt_init() */ + + +static union dns_any *dns_opt_initany(union dns_any *any, size_t size) { + return dns_opt_init(&any->opt, size), any; +} /* dns_opt_initany() */ + + +int dns_opt_parse(struct dns_opt *opt, struct dns_rr *rr, struct dns_packet *P) { + const struct dns_buf src = DNS_B_FROM(&P->data[rr->rd.p], rr->rd.len); + struct dns_buf dst = DNS_B_INTO(opt->data, opt->size); + int error; + + opt->rcode = 0xfff & ((rr->ttl >> 20) | dns_header(P)->rcode); + opt->version = 0xff & (rr->ttl >> 16); + opt->flags = 0xffff & rr->ttl; + opt->maxudp = 0xffff & rr->class; + + while (src.p < src.pe) { + int code, len; + + if (-1 == (code = dns_b_get16(&src, -1))) + return src.error; + if (-1 == (len = dns_b_get16(&src, -1))) + return src.error; + + switch (code) { + default: + dns_b_put16(&dst, code); + dns_b_put16(&dst, len); + if ((error = dns_b_move(&dst, &src, len))) + return error; + break; + } + } + + return 0; +} /* dns_opt_parse() */ + + +int dns_opt_push(struct dns_packet *P, struct dns_opt *opt) { + const struct dns_buf src = DNS_B_FROM(opt->data, opt->len); + struct dns_buf dst = DNS_B_INTO(&P->data[P->end], (P->size - P->end)); + int error; + + /* rdata length (see below) */ + if ((error = dns_b_put16(&dst, 0))) + goto error; + + /* ... push known options here */ + + /* push opaque option data */ + if ((error = dns_b_move(&dst, &src, (size_t)(src.pe - src.p)))) + goto error; + + /* rdata length */ + if ((error = dns_b_pput16(&dst, dns_b_tell(&dst) - 2, 0))) + goto error; + +#if !DNS_DEBUG_OPT_FORMERR + P->end += dns_b_tell(&dst); +#endif + + return 0; +error: + return error; +} /* dns_opt_push() */ + + +int dns_opt_cmp(const struct dns_opt *a, const struct dns_opt *b) { + (void)a; + (void)b; + + return -1; +} /* dns_opt_cmp() */ + + +size_t dns_opt_print(void *_dst, size_t lim, struct dns_opt *opt) { + struct dns_buf dst = DNS_B_INTO(_dst, lim); + size_t p; + + dns_b_putc(&dst, '"'); + + for (p = 0; p < opt->len; p++) { + dns_b_putc(&dst, '\\'); + dns_b_fmtju(&dst, opt->data[p], 3); + } + + dns_b_putc(&dst, '"'); + + return dns_b_strllen(&dst); +} /* dns_opt_print() */ + + +int dns_ptr_parse(struct dns_ptr *ptr, struct dns_rr *rr, struct dns_packet *P) { + return dns_ns_parse((struct dns_ns *)ptr, rr, P); +} /* dns_ptr_parse() */ + + +int dns_ptr_push(struct dns_packet *P, struct dns_ptr *ptr) { + return dns_ns_push(P, (struct dns_ns *)ptr); +} /* dns_ptr_push() */ + + +size_t dns_ptr_qname(void *dst, size_t lim, int af, void *addr) { + switch (af) { + case AF_INET6: + return dns_aaaa_arpa(dst, lim, addr); + case AF_INET: + return dns_a_arpa(dst, lim, addr); + default: { + struct dns_a a; + a.addr.s_addr = INADDR_NONE; + return dns_a_arpa(dst, lim, &a); + } + } +} /* dns_ptr_qname() */ + + +int dns_ptr_cmp(const struct dns_ptr *a, const struct dns_ptr *b) { + return strcasecmp(a->host, b->host); +} /* dns_ptr_cmp() */ + + +size_t dns_ptr_print(void *dst, size_t lim, struct dns_ptr *ptr) { + return dns_ns_print(dst, lim, (struct dns_ns *)ptr); +} /* dns_ptr_print() */ + + +size_t dns_ptr_cname(void *dst, size_t lim, struct dns_ptr *ptr) { + return dns_strlcpy(dst, ptr->host, lim); +} /* dns_ptr_cname() */ + + +int dns_sshfp_parse(struct dns_sshfp *fp, struct dns_rr *rr, struct dns_packet *P) { + unsigned p = rr->rd.p, pe = rr->rd.p + rr->rd.len; + + if (pe - p < 2) + return DNS_EILLEGAL; + + fp->algo = P->data[p++]; + fp->type = P->data[p++]; + + switch (fp->type) { + case DNS_SSHFP_SHA1: + if (pe - p < sizeof fp->digest.sha1) + return DNS_EILLEGAL; + + memcpy(fp->digest.sha1, &P->data[p], sizeof fp->digest.sha1); + + break; + default: + break; + } /* switch() */ + + return 0; +} /* dns_sshfp_parse() */ + + +int dns_sshfp_push(struct dns_packet *P, struct dns_sshfp *fp) { + unsigned p = P->end, pe = P->size, n; + + if (pe - p < 4) + return DNS_ENOBUFS; + + p += 2; + P->data[p++] = 0xff & fp->algo; + P->data[p++] = 0xff & fp->type; + + switch (fp->type) { + case DNS_SSHFP_SHA1: + if (pe - p < sizeof fp->digest.sha1) + return DNS_ENOBUFS; + + memcpy(&P->data[p], fp->digest.sha1, sizeof fp->digest.sha1); + p += sizeof fp->digest.sha1; + + break; + default: + return DNS_EILLEGAL; + } /* switch() */ + + n = p - P->end - 2; + P->data[P->end++] = 0xff & (n >> 8); + P->data[P->end++] = 0xff & (n >> 0); + P->end = p; + + return 0; +} /* dns_sshfp_push() */ + + +int dns_sshfp_cmp(const struct dns_sshfp *a, const struct dns_sshfp *b) { + int cmp; + + if ((cmp = a->algo - b->algo) || (cmp = a->type - b->type)) + return cmp; + + switch (a->type) { + case DNS_SSHFP_SHA1: + return memcmp(a->digest.sha1, b->digest.sha1, sizeof a->digest.sha1); + default: + return 0; + } /* switch() */ + + /* NOT REACHED */ +} /* dns_sshfp_cmp() */ + + +size_t dns_sshfp_print(void *_dst, size_t lim, struct dns_sshfp *fp) { + static const unsigned char hex[16] = "0123456789abcdef"; + struct dns_buf dst = DNS_B_INTO(_dst, lim); + size_t i; + + dns_b_fmtju(&dst, fp->algo, 0); + dns_b_putc(&dst, ' '); + dns_b_fmtju(&dst, fp->type, 0); + dns_b_putc(&dst, ' '); + + switch (fp->type) { + case DNS_SSHFP_SHA1: + for (i = 0; i < sizeof fp->digest.sha1; i++) { + dns_b_putc(&dst, hex[0x0f & (fp->digest.sha1[i] >> 4)]); + dns_b_putc(&dst, hex[0x0f & (fp->digest.sha1[i] >> 0)]); + } + + break; + default: + dns_b_putc(&dst, '0'); + + break; + } /* switch() */ + + return dns_b_strllen(&dst); +} /* dns_sshfp_print() */ + + +struct dns_txt *dns_txt_init(struct dns_txt *txt, size_t size) { + assert(size > offsetof(struct dns_txt, data)); + + txt->size = size - offsetof(struct dns_txt, data); + txt->len = 0; + + return txt; +} /* dns_txt_init() */ + + +static union dns_any *dns_txt_initany(union dns_any *any, size_t size) { + /* NB: union dns_any is already initialized as struct dns_txt */ + (void)size; + return any; +} /* dns_txt_initany() */ + + +int dns_txt_parse(struct dns_txt *txt, struct dns_rr *rr, struct dns_packet *P) { + struct { unsigned char *b; size_t p, end; } dst, src; + unsigned n; + + dst.b = txt->data; + dst.p = 0; + dst.end = txt->size; + + src.b = P->data; + src.p = rr->rd.p; + src.end = src.p + rr->rd.len; + + while (src.p < src.end) { + n = 0xff & P->data[src.p++]; + + if (src.end - src.p < n || dst.end - dst.p < n) + return DNS_EILLEGAL; + + memcpy(&dst.b[dst.p], &src.b[src.p], n); + + dst.p += n; + src.p += n; + } + + txt->len = dst.p; + + return 0; +} /* dns_txt_parse() */ + + +int dns_txt_push(struct dns_packet *P, struct dns_txt *txt) { + struct { unsigned char *b; size_t p, end; } dst, src; + unsigned n; + + dst.b = P->data; + dst.p = P->end; + dst.end = P->size; + + src.b = txt->data; + src.p = 0; + src.end = txt->len; + + if (dst.end - dst.p < 2) + return DNS_ENOBUFS; + + n = txt->len + ((txt->len + 254) / 255); + + dst.b[dst.p++] = 0xff & (n >> 8); + dst.b[dst.p++] = 0xff & (n >> 0); + + while (src.p < src.end) { + n = DNS_PP_MIN(255, src.end - src.p); + + if (dst.p >= dst.end) + return DNS_ENOBUFS; + + dst.b[dst.p++] = n; + + if (dst.end - dst.p < n) + return DNS_ENOBUFS; + + memcpy(&dst.b[dst.p], &src.b[src.p], n); + + dst.p += n; + src.p += n; + } + + P->end = dst.p; + + return 0; +} /* dns_txt_push() */ + + +int dns_txt_cmp(const struct dns_txt *a, const struct dns_txt *b) { + (void)a; + (void)b; + + return -1; +} /* dns_txt_cmp() */ + + +size_t dns_txt_print(void *_dst, size_t lim, struct dns_txt *txt) { + struct dns_buf src = DNS_B_FROM(txt->data, txt->len); + struct dns_buf dst = DNS_B_INTO(_dst, lim); + unsigned i; + + if (src.p < src.pe) { + do { + dns_b_putc(&dst, '"'); + + for (i = 0; i < 256 && src.p < src.pe; i++, src.p++) { + if (*src.p < 32 || *src.p > 126 || *src.p == '"' || *src.p == '\\') { + dns_b_putc(&dst, '\\'); + dns_b_fmtju(&dst, *src.p, 3); + } else { + dns_b_putc(&dst, *src.p); + } + } + + dns_b_putc(&dst, '"'); + dns_b_putc(&dst, ' '); + } while (src.p < src.pe); + + dns_b_popc(&dst); + } else { + dns_b_putc(&dst, '"'); + dns_b_putc(&dst, '"'); + } + + return dns_b_strllen(&dst); +} /* dns_txt_print() */ + + +static const struct dns_rrtype { + enum dns_type type; + const char *name; + union dns_any *(*init)(union dns_any *, size_t); + int (*parse)(); + int (*push)(); + int (*cmp)(); + size_t (*print)(); + size_t (*cname)(); +} dns_rrtypes[] = { + { DNS_T_A, "A", 0, &dns_a_parse, &dns_a_push, &dns_a_cmp, &dns_a_print, 0, }, + { DNS_T_AAAA, "AAAA", 0, &dns_aaaa_parse, &dns_aaaa_push, &dns_aaaa_cmp, &dns_aaaa_print, 0, }, + { DNS_T_MX, "MX", 0, &dns_mx_parse, &dns_mx_push, &dns_mx_cmp, &dns_mx_print, &dns_mx_cname, }, + { DNS_T_NS, "NS", 0, &dns_ns_parse, &dns_ns_push, &dns_ns_cmp, &dns_ns_print, &dns_ns_cname, }, + { DNS_T_CNAME, "CNAME", 0, &dns_cname_parse, &dns_cname_push, &dns_cname_cmp, &dns_cname_print, &dns_cname_cname, }, + { DNS_T_SOA, "SOA", 0, &dns_soa_parse, &dns_soa_push, &dns_soa_cmp, &dns_soa_print, 0, }, + { DNS_T_SRV, "SRV", 0, &dns_srv_parse, &dns_srv_push, &dns_srv_cmp, &dns_srv_print, &dns_srv_cname, }, + { DNS_T_OPT, "OPT", &dns_opt_initany, &dns_opt_parse, &dns_opt_push, &dns_opt_cmp, &dns_opt_print, 0, }, + { DNS_T_PTR, "PTR", 0, &dns_ptr_parse, &dns_ptr_push, &dns_ptr_cmp, &dns_ptr_print, &dns_ptr_cname, }, + { DNS_T_TXT, "TXT", &dns_txt_initany, &dns_txt_parse, &dns_txt_push, &dns_txt_cmp, &dns_txt_print, 0, }, + { DNS_T_SPF, "SPF", &dns_txt_initany, &dns_txt_parse, &dns_txt_push, &dns_txt_cmp, &dns_txt_print, 0, }, + { DNS_T_SSHFP, "SSHFP", 0, &dns_sshfp_parse, &dns_sshfp_push, &dns_sshfp_cmp, &dns_sshfp_print, 0, }, + { DNS_T_AXFR, "AXFR", 0, 0, 0, 0, 0, 0, }, +}; /* dns_rrtypes[] */ + +static const struct dns_rrtype *dns_rrtype(enum dns_type type) { + const struct dns_rrtype *t; + + for (t = dns_rrtypes; t < endof(dns_rrtypes); t++) { + if (t->type == type && t->parse) { + return t; + } + } + + return NULL; +} /* dns_rrtype() */ + + +union dns_any *dns_any_init(union dns_any *any, size_t size) { + dns_static_assert(dns_same_type(any->txt, any->rdata, 1), "unexpected rdata type"); + return (union dns_any *)dns_txt_init(&any->rdata, size); +} /* dns_any_init() */ + + +static size_t dns_any_sizeof(union dns_any *any) { + dns_static_assert(dns_same_type(any->txt, any->rdata, 1), "unexpected rdata type"); + return offsetof(struct dns_txt, data) + any->rdata.size; +} /* dns_any_sizeof() */ + +static union dns_any *dns_any_reinit(union dns_any *any, const struct dns_rrtype *t) { + return (t->init)? t->init(any, dns_any_sizeof(any)) : any; +} /* dns_any_reinit() */ + +int dns_any_parse(union dns_any *any, struct dns_rr *rr, struct dns_packet *P) { + const struct dns_rrtype *t; + + if ((t = dns_rrtype(rr->type))) + return t->parse(dns_any_reinit(any, t), rr, P); + + if (rr->rd.len > any->rdata.size) + return DNS_EILLEGAL; + + memcpy(any->rdata.data, &P->data[rr->rd.p], rr->rd.len); + any->rdata.len = rr->rd.len; + + return 0; +} /* dns_any_parse() */ + + +int dns_any_push(struct dns_packet *P, union dns_any *any, enum dns_type type) { + const struct dns_rrtype *t; + + if ((t = dns_rrtype(type))) + return t->push(P, any); + + if (P->size - P->end < any->rdata.len + 2) + return DNS_ENOBUFS; + + P->data[P->end++] = 0xff & (any->rdata.len >> 8); + P->data[P->end++] = 0xff & (any->rdata.len >> 0); + + memcpy(&P->data[P->end], any->rdata.data, any->rdata.len); + P->end += any->rdata.len; + + return 0; +} /* dns_any_push() */ + + +int dns_any_cmp(const union dns_any *a, enum dns_type x, const union dns_any *b, enum dns_type y) { + const struct dns_rrtype *t; + int cmp; + + if ((cmp = x - y)) + return cmp; + + if ((t = dns_rrtype(x))) + return t->cmp(a, b); + + return -1; +} /* dns_any_cmp() */ + + +size_t dns_any_print(void *_dst, size_t lim, union dns_any *any, enum dns_type type) { + const struct dns_rrtype *t; + struct dns_buf src, dst; + + if ((t = dns_rrtype(type))) + return t->print(_dst, lim, any); + + dns_b_from(&src, any->rdata.data, any->rdata.len); + dns_b_into(&dst, _dst, lim); + + dns_b_putc(&dst, '"'); + + while (src.p < src.pe) { + dns_b_putc(&dst, '\\'); + dns_b_fmtju(&dst, *src.p++, 3); + } + + dns_b_putc(&dst, '"'); + + return dns_b_strllen(&dst); +} /* dns_any_print() */ + + +size_t dns_any_cname(void *dst, size_t lim, union dns_any *any, enum dns_type type) { + const struct dns_rrtype *t; + + if ((t = dns_rrtype(type)) && t->cname) + return t->cname(dst, lim, any); + + return 0; +} /* dns_any_cname() */ + + +/* + * H O S T S R O U T I N E S + * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +struct dns_hosts { + struct dns_hosts_entry { + char host[DNS_D_MAXNAME + 1]; + char arpa[73 + 1]; + + int af; + + union { + struct in_addr a4; + struct in6_addr a6; + } addr; + + _Bool alias; + + struct dns_hosts_entry *next; + } *head, **tail; + + dns_atomic_t refcount; +}; /* struct dns_hosts */ + + +struct dns_hosts *dns_hosts_open(int *error) { + static const struct dns_hosts hosts_initializer = { .refcount = 1 }; + struct dns_hosts *hosts; + + if (!(hosts = malloc(sizeof *hosts))) + goto syerr; + + *hosts = hosts_initializer; + + hosts->tail = &hosts->head; + + return hosts; +syerr: + *error = dns_syerr(); + + free(hosts); + + return 0; +} /* dns_hosts_open() */ + + +void dns_hosts_close(struct dns_hosts *hosts) { + struct dns_hosts_entry *ent, *xnt; + + if (!hosts || 1 != dns_hosts_release(hosts)) + return; + + for (ent = hosts->head; ent; ent = xnt) { + xnt = ent->next; + + free(ent); + } + + free(hosts); + + return; +} /* dns_hosts_close() */ + + +dns_refcount_t dns_hosts_acquire(struct dns_hosts *hosts) { + return dns_atomic_fetch_add(&hosts->refcount); +} /* dns_hosts_acquire() */ + + +dns_refcount_t dns_hosts_release(struct dns_hosts *hosts) { + return dns_atomic_fetch_sub(&hosts->refcount); +} /* dns_hosts_release() */ + + +struct dns_hosts *dns_hosts_mortal(struct dns_hosts *hosts) { + if (hosts) + dns_hosts_release(hosts); + + return hosts; +} /* dns_hosts_mortal() */ + + +struct dns_hosts *dns_hosts_local(int *error_) { + struct dns_hosts *hosts; + int error; + + if (!(hosts = dns_hosts_open(&error))) + goto error; + + if ((error = dns_hosts_loadpath(hosts, "/etc/hosts"))) + goto error; + + return hosts; +error: + *error_ = error; + + dns_hosts_close(hosts); + + return 0; +} /* dns_hosts_local() */ + + +#define dns_hosts_issep(ch) (dns_isspace(ch)) +#define dns_hosts_iscom(ch) ((ch) == '#' || (ch) == ';') + +int dns_hosts_loadfile(struct dns_hosts *hosts, FILE *fp) { + struct dns_hosts_entry ent; + char word[DNS_PP_MAX(INET6_ADDRSTRLEN, DNS_D_MAXNAME) + 1]; + unsigned wp, wc, skip; + int ch, error; + + rewind(fp); + + do { + memset(&ent, '\0', sizeof ent); + wc = 0; + skip = 0; + + do { + memset(word, '\0', sizeof word); + wp = 0; + + while (EOF != (ch = fgetc(fp)) && ch != '\n') { + skip |= !!dns_hosts_iscom(ch); + + if (skip) + continue; + + if (dns_hosts_issep(ch)) + break; + + if (wp < sizeof word - 1) + word[wp] = ch; + wp++; + } + + if (!wp) + continue; + + wc++; + + switch (wc) { + case 0: + break; + case 1: + ent.af = (strchr(word, ':'))? AF_INET6 : AF_INET; + skip = (1 != dns_inet_pton(ent.af, word, &ent.addr)); + + break; + default: + if (!wp) + break; + + dns_d_anchor(ent.host, sizeof ent.host, word, wp); + + if ((error = dns_hosts_insert(hosts, ent.af, &ent.addr, ent.host, (wc > 2)))) + return error; + + break; + } /* switch() */ + } while (ch != EOF && ch != '\n'); + } while (ch != EOF); + + return 0; +} /* dns_hosts_loadfile() */ + + +int dns_hosts_loadpath(struct dns_hosts *hosts, const char *path) { + FILE *fp; + int error; + + if (!(fp = dns_fopen(path, "rt", &error))) + return error; + + error = dns_hosts_loadfile(hosts, fp); + + fclose(fp); + + return error; +} /* dns_hosts_loadpath() */ + + +int dns_hosts_dump(struct dns_hosts *hosts, FILE *fp) { + struct dns_hosts_entry *ent, *xnt; + char addr[INET6_ADDRSTRLEN + 1]; + unsigned i; + + for (ent = hosts->head; ent; ent = xnt) { + xnt = ent->next; + + dns_inet_ntop(ent->af, &ent->addr, addr, sizeof addr); + + fputs(addr, fp); + + for (i = strlen(addr); i < INET_ADDRSTRLEN; i++) + fputc(' ', fp); + + fputc(' ', fp); + + fputs(ent->host, fp); + fputc('\n', fp); + } + + return 0; +} /* dns_hosts_dump() */ + + +int dns_hosts_insert(struct dns_hosts *hosts, int af, const void *addr, const void *host, _Bool alias) { + struct dns_hosts_entry *ent; + int error; + + if (!(ent = malloc(sizeof *ent))) + goto syerr; + + dns_d_anchor(ent->host, sizeof ent->host, host, strlen(host)); + + switch ((ent->af = af)) { + case AF_INET6: + memcpy(&ent->addr.a6, addr, sizeof ent->addr.a6); + + dns_aaaa_arpa(ent->arpa, sizeof ent->arpa, addr); + + break; + case AF_INET: + memcpy(&ent->addr.a4, addr, sizeof ent->addr.a4); + + dns_a_arpa(ent->arpa, sizeof ent->arpa, addr); + + break; + default: + error = EINVAL; + + goto error; + } /* switch() */ + + ent->alias = alias; + + ent->next = 0; + *hosts->tail = ent; + hosts->tail = &ent->next; + + return 0; +syerr: + error = dns_syerr(); +error: + free(ent); + + return error; +} /* dns_hosts_insert() */ + + +struct dns_packet *dns_hosts_query(struct dns_hosts *hosts, struct dns_packet *Q, int *error_) { + struct dns_packet *P = dns_p_new(512); + struct dns_packet *A = 0; + struct dns_rr rr; + struct dns_hosts_entry *ent; + int error, af; + char qname[DNS_D_MAXNAME + 1]; + size_t qlen; + + if ((error = dns_rr_parse(&rr, 12, Q))) + goto error; + + if (!(qlen = dns_d_expand(qname, sizeof qname, rr.dn.p, Q, &error))) + goto error; + else if (qlen >= sizeof qname) + goto toolong; + + if ((error = dns_p_push(P, DNS_S_QD, qname, qlen, rr.type, rr.class, 0, 0))) + goto error; + + switch (rr.type) { + case DNS_T_PTR: + for (ent = hosts->head; ent; ent = ent->next) { + if (ent->alias || 0 != strcasecmp(qname, ent->arpa)) + continue; + + if ((error = dns_p_push(P, DNS_S_AN, qname, qlen, rr.type, rr.class, 0, ent->host))) + goto error; + } + + break; + case DNS_T_AAAA: + af = AF_INET6; + + goto loop; + case DNS_T_A: + af = AF_INET; + +loop: for (ent = hosts->head; ent; ent = ent->next) { + if (ent->af != af || 0 != strcasecmp(qname, ent->host)) + continue; + + if ((error = dns_p_push(P, DNS_S_AN, qname, qlen, rr.type, rr.class, 0, &ent->addr))) + goto error; + } + + break; + default: + break; + } /* switch() */ + + + if (!(A = dns_p_copy(dns_p_make(P->end, &error), P))) + goto error; + + return A; +toolong: + error = DNS_EILLEGAL; +error: + *error_ = error; + + dns_p_free(A); + + return 0; +} /* dns_hosts_query() */ + + +/* + * R E S O L V . C O N F R O U T I N E S + * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +struct dns_resolv_conf *dns_resconf_open(int *error) { + static const struct dns_resolv_conf resconf_initializer = { + .lookup = "bf", + .family = { AF_INET, AF_INET6 }, + .options = { .ndots = 1, .timeout = 5, .attempts = 2, .tcp = DNS_RESCONF_TCP_ENABLE, }, + .iface = { .ss_family = AF_INET }, + }; + struct dns_resolv_conf *resconf; + struct sockaddr_in *sin; + + if (!(resconf = malloc(sizeof *resconf))) + goto syerr; + + *resconf = resconf_initializer; + + sin = (struct sockaddr_in *)&resconf->nameserver[0]; + sin->sin_family = AF_INET; + sin->sin_addr.s_addr = INADDR_ANY; + sin->sin_port = htons(53); +#if defined(SA_LEN) + sin->sin_len = sizeof *sin; +#endif + + if (0 != gethostname(resconf->search[0], sizeof resconf->search[0])) + goto syerr; + + dns_d_anchor(resconf->search[0], sizeof resconf->search[0], resconf->search[0], strlen(resconf->search[0])); + dns_d_cleave(resconf->search[0], sizeof resconf->search[0], resconf->search[0], strlen(resconf->search[0])); + + /* + * XXX: If gethostname() returned a string without any label + * separator, then search[0][0] should be NUL. + */ + + dns_resconf_acquire(resconf); + + return resconf; +syerr: + *error = dns_syerr(); + + free(resconf); + + return 0; +} /* dns_resconf_open() */ + + +void dns_resconf_close(struct dns_resolv_conf *resconf) { + if (!resconf || 1 != dns_resconf_release(resconf)) + return /* void */; + + free(resconf); +} /* dns_resconf_close() */ + + +dns_refcount_t dns_resconf_acquire(struct dns_resolv_conf *resconf) { + return dns_atomic_fetch_add(&resconf->_.refcount); +} /* dns_resconf_acquire() */ + + +dns_refcount_t dns_resconf_release(struct dns_resolv_conf *resconf) { + return dns_atomic_fetch_sub(&resconf->_.refcount); +} /* dns_resconf_release() */ + + +struct dns_resolv_conf *dns_resconf_mortal(struct dns_resolv_conf *resconf) { + if (resconf) + dns_resconf_release(resconf); + + return resconf; +} /* dns_resconf_mortal() */ + + +struct dns_resolv_conf *dns_resconf_local(int *error_) { + struct dns_resolv_conf *resconf; + int error; + + if (!(resconf = dns_resconf_open(&error))) + goto error; + + if ((error = dns_resconf_loadpath(resconf, "/etc/resolv.conf"))) { + /* + * NOTE: Both the glibc and BIND9 resolvers ignore a missing + * /etc/resolv.conf, defaulting to a nameserver of + * 127.0.0.1. See also dns_hints_insert_resconf, and the + * default initialization of nameserver[0] in + * dns_resconf_open. + */ + if (error != ENOENT) + goto error; + } + + if ((error = dns_nssconf_loadpath(resconf, "/etc/nsswitch.conf"))) { + if (error != ENOENT) + goto error; + } + + return resconf; +error: + *error_ = error; + + dns_resconf_close(resconf); + + return 0; +} /* dns_resconf_local() */ + + +struct dns_resolv_conf *dns_resconf_root(int *error) { + struct dns_resolv_conf *resconf; + + if ((resconf = dns_resconf_local(error))) + resconf->options.recurse = 1; + + return resconf; +} /* dns_resconf_root() */ + + +static time_t dns_resconf_timeout(const struct dns_resolv_conf *resconf) { + return (time_t)DNS_PP_MIN(INT_MAX, resconf->options.timeout); +} /* dns_resconf_timeout() */ + + +enum dns_resconf_keyword { + DNS_RESCONF_NAMESERVER, + DNS_RESCONF_DOMAIN, + DNS_RESCONF_SEARCH, + DNS_RESCONF_LOOKUP, + DNS_RESCONF_FILE, + DNS_RESCONF_BIND, + DNS_RESCONF_CACHE, + DNS_RESCONF_FAMILY, + DNS_RESCONF_INET4, + DNS_RESCONF_INET6, + DNS_RESCONF_OPTIONS, + DNS_RESCONF_EDNS0, + DNS_RESCONF_NDOTS, + DNS_RESCONF_TIMEOUT, + DNS_RESCONF_ATTEMPTS, + DNS_RESCONF_ROTATE, + DNS_RESCONF_RECURSE, + DNS_RESCONF_SMART, + DNS_RESCONF_TCP, + DNS_RESCONF_TCPx, + DNS_RESCONF_INTERFACE, + DNS_RESCONF_ZERO, + DNS_RESCONF_ONE, + DNS_RESCONF_ENABLE, + DNS_RESCONF_ONLY, + DNS_RESCONF_DISABLE, +}; /* enum dns_resconf_keyword */ + +static enum dns_resconf_keyword dns_resconf_keyword(const char *word) { + static const char *words[] = { + [DNS_RESCONF_NAMESERVER] = "nameserver", + [DNS_RESCONF_DOMAIN] = "domain", + [DNS_RESCONF_SEARCH] = "search", + [DNS_RESCONF_LOOKUP] = "lookup", + [DNS_RESCONF_FILE] = "file", + [DNS_RESCONF_BIND] = "bind", + [DNS_RESCONF_CACHE] = "cache", + [DNS_RESCONF_FAMILY] = "family", + [DNS_RESCONF_INET4] = "inet4", + [DNS_RESCONF_INET6] = "inet6", + [DNS_RESCONF_OPTIONS] = "options", + [DNS_RESCONF_EDNS0] = "edns0", + [DNS_RESCONF_ROTATE] = "rotate", + [DNS_RESCONF_RECURSE] = "recurse", + [DNS_RESCONF_SMART] = "smart", + [DNS_RESCONF_TCP] = "tcp", + [DNS_RESCONF_INTERFACE] = "interface", + [DNS_RESCONF_ZERO] = "0", + [DNS_RESCONF_ONE] = "1", + [DNS_RESCONF_ENABLE] = "enable", + [DNS_RESCONF_ONLY] = "only", + [DNS_RESCONF_DISABLE] = "disable", + }; + unsigned i; + + for (i = 0; i < lengthof(words); i++) { + if (words[i] && 0 == strcasecmp(words[i], word)) + return i; + } + + if (0 == strncasecmp(word, "ndots:", sizeof "ndots:" - 1)) + return DNS_RESCONF_NDOTS; + + if (0 == strncasecmp(word, "timeout:", sizeof "timeout:" - 1)) + return DNS_RESCONF_TIMEOUT; + + if (0 == strncasecmp(word, "attempts:", sizeof "attempts:" - 1)) + return DNS_RESCONF_ATTEMPTS; + + if (0 == strncasecmp(word, "tcp:", sizeof "tcp:" - 1)) + return DNS_RESCONF_TCPx; + + return -1; +} /* dns_resconf_keyword() */ + + +/** OpenBSD-style "[1.2.3.4]:53" nameserver syntax */ +int dns_resconf_pton(struct sockaddr_storage *ss, const char *src) { + struct { char buf[128], *p; } addr = { "", addr.buf }; + unsigned short port = 0; + int ch, af = AF_INET, error; + + while ((ch = *src++)) { + switch (ch) { + case ' ': + /* FALL THROUGH */ + case '\t': + break; + case '[': + break; + case ']': + while ((ch = *src++)) { + if (dns_isdigit(ch)) { + port *= 10; + port += ch - '0'; + } + } + + goto inet; + case ':': + af = AF_INET6; + + /* FALL THROUGH */ + default: + if (addr.p < endof(addr.buf) - 1) + *addr.p++ = ch; + + break; + } /* switch() */ + } /* while() */ +inet: + + if ((error = dns_pton(af, addr.buf, dns_sa_addr(af, ss, NULL)))) + return error; + + port = (!port)? 53 : port; + *dns_sa_port(af, ss) = htons(port); + dns_sa_family(ss) = af; + + return 0; +} /* dns_resconf_pton() */ + +#define dns_resconf_issep(ch) (dns_isspace(ch) || (ch) == ',') +#define dns_resconf_iscom(ch) ((ch) == '#' || (ch) == ';') + +int dns_resconf_loadfile(struct dns_resolv_conf *resconf, FILE *fp) { + unsigned sa_count = 0; + char words[6][DNS_D_MAXNAME + 1]; + unsigned wp, wc, i, j, n; + int ch, error; + + rewind(fp); + + do { + memset(words, '\0', sizeof words); + wp = 0; + wc = 0; + + while (EOF != (ch = getc(fp)) && ch != '\n') { + if (dns_resconf_issep(ch)) { + if (wp > 0) { + wp = 0; + + if (++wc >= lengthof(words)) + goto skip; + } + } else if (dns_resconf_iscom(ch)) { +skip: + do { + ch = getc(fp); + } while (ch != EOF && ch != '\n'); + + break; + } else if (wp < sizeof words[wc] - 1) { + words[wc][wp++] = ch; + } else { + wp = 0; /* drop word */ + goto skip; + } + } + + if (wp > 0) + wc++; + + if (wc < 2) + continue; + + switch (dns_resconf_keyword(words[0])) { + case DNS_RESCONF_NAMESERVER: + if (sa_count >= lengthof(resconf->nameserver)) + continue; + + if ((error = dns_resconf_pton(&resconf->nameserver[sa_count], words[1]))) + continue; + + sa_count++; + + break; + case DNS_RESCONF_DOMAIN: + case DNS_RESCONF_SEARCH: + memset(resconf->search, '\0', sizeof resconf->search); + + for (i = 1, j = 0; i < wc && j < lengthof(resconf->search); i++, j++) + dns_d_anchor(resconf->search[j], sizeof resconf->search[j], words[i], strlen(words[i])); + + break; + case DNS_RESCONF_LOOKUP: + for (i = 1, j = 0; i < wc && j < lengthof(resconf->lookup); i++) { + switch (dns_resconf_keyword(words[i])) { + case DNS_RESCONF_FILE: + resconf->lookup[j++] = 'f'; + + break; + case DNS_RESCONF_BIND: + resconf->lookup[j++] = 'b'; + + break; + case DNS_RESCONF_CACHE: + resconf->lookup[j++] = 'c'; + + break; + default: + break; + } /* switch() */ + } /* for() */ + + break; + case DNS_RESCONF_FAMILY: + for (i = 1, j = 0; i < wc && j < lengthof(resconf->family); i++) { + switch (dns_resconf_keyword(words[i])) { + case DNS_RESCONF_INET4: + resconf->family[j++] = AF_INET; + + break; + case DNS_RESCONF_INET6: + resconf->family[j++] = AF_INET6; + + break; + default: + break; + } + } + + break; + case DNS_RESCONF_OPTIONS: + for (i = 1; i < wc; i++) { + switch (dns_resconf_keyword(words[i])) { + case DNS_RESCONF_EDNS0: + resconf->options.edns0 = 1; + + break; + case DNS_RESCONF_NDOTS: + for (j = sizeof "ndots:" - 1, n = 0; dns_isdigit(words[i][j]); j++) { + n *= 10; + n += words[i][j] - '0'; + } /* for() */ + + resconf->options.ndots = n; + + break; + case DNS_RESCONF_TIMEOUT: + for (j = sizeof "timeout:" - 1, n = 0; dns_isdigit(words[i][j]); j++) { + n *= 10; + n += words[i][j] - '0'; + } /* for() */ + + resconf->options.timeout = n; + + break; + case DNS_RESCONF_ATTEMPTS: + for (j = sizeof "attempts:" - 1, n = 0; dns_isdigit(words[i][j]); j++) { + n *= 10; + n += words[i][j] - '0'; + } /* for() */ + + resconf->options.attempts = n; + + break; + case DNS_RESCONF_ROTATE: + resconf->options.rotate = 1; + + break; + case DNS_RESCONF_RECURSE: + resconf->options.recurse = 1; + + break; + case DNS_RESCONF_SMART: + resconf->options.smart = 1; + + break; + case DNS_RESCONF_TCP: + resconf->options.tcp = DNS_RESCONF_TCP_ONLY; + + break; + case DNS_RESCONF_TCPx: + switch (dns_resconf_keyword(&words[i][sizeof "tcp:" - 1])) { + case DNS_RESCONF_ENABLE: + resconf->options.tcp = DNS_RESCONF_TCP_ENABLE; + + break; + case DNS_RESCONF_ONE: + case DNS_RESCONF_ONLY: + resconf->options.tcp = DNS_RESCONF_TCP_ONLY; + + break; + case DNS_RESCONF_ZERO: + case DNS_RESCONF_DISABLE: + resconf->options.tcp = DNS_RESCONF_TCP_DISABLE; + + break; + default: + break; + } /* switch() */ + + break; + default: + break; + } /* switch() */ + } /* for() */ + + break; + case DNS_RESCONF_INTERFACE: + for (i = 0, n = 0; dns_isdigit(words[2][i]); i++) { + n *= 10; + n += words[2][i] - '0'; + } + + dns_resconf_setiface(resconf, words[1], n); + + break; + default: + break; + } /* switch() */ + } while (ch != EOF); + + return 0; +} /* dns_resconf_loadfile() */ + + +int dns_resconf_loadpath(struct dns_resolv_conf *resconf, const char *path) { + FILE *fp; + int error; + + if (!(fp = dns_fopen(path, "rt", &error))) + return error; + + error = dns_resconf_loadfile(resconf, fp); + + fclose(fp); + + return error; +} /* dns_resconf_loadpath() */ + + +struct dns_anyconf { + char *token[16]; + unsigned count; + char buffer[1024], *tp, *cp; +}; /* struct dns_anyconf */ + + +static void dns_anyconf_reset(struct dns_anyconf *cf) { + cf->count = 0; + cf->tp = cf->cp = cf->buffer; +} /* dns_anyconf_reset() */ + + +static int dns_anyconf_push(struct dns_anyconf *cf) { + if (!(cf->cp < endof(cf->buffer) && cf->count < lengthof(cf->token))) + return ENOMEM; + + *cf->cp++ = '\0'; + cf->token[cf->count++] = cf->tp; + cf->tp = cf->cp; + + return 0; +} /* dns_anyconf_push() */ + + +static void dns_anyconf_pop(struct dns_anyconf *cf) { + if (cf->count > 0) { + --cf->count; + cf->tp = cf->cp = cf->token[cf->count]; + cf->token[cf->count] = 0; + } +} /* dns_anyconf_pop() */ + + +static int dns_anyconf_addc(struct dns_anyconf *cf, int ch) { + if (!(cf->cp < endof(cf->buffer))) + return ENOMEM; + + *cf->cp++ = ch; + + return 0; +} /* dns_anyconf_addc() */ + + +static _Bool dns_anyconf_match(const char *pat, int mc) { + _Bool match; + int pc; + + if (*pat == '^') { + match = 0; + ++pat; + } else { + match = 1; + } + + while ((pc = *(const unsigned char *)pat++)) { + switch (pc) { + case '%': + if (!(pc = *(const unsigned char *)pat++)) + return !match; + + switch (pc) { + case 'a': + if (dns_isalpha(mc)) + return match; + break; + case 'd': + if (dns_isdigit(mc)) + return match; + break; + case 'w': + if (dns_isalnum(mc)) + return match; + break; + case 's': + if (dns_isspace(mc)) + return match; + break; + default: + if (mc == pc) + return match; + break; + } /* switch() */ + + break; + default: + if (mc == pc) + return match; + break; + } /* switch() */ + } /* while() */ + + return !match; +} /* dns_anyconf_match() */ + + +static int dns_anyconf_peek(FILE *fp) { + int ch; + ch = getc(fp); + ungetc(ch, fp); + return ch; +} /* dns_anyconf_peek() */ + + +static size_t dns_anyconf_skip(const char *pat, FILE *fp) { + size_t count = 0; + int ch; + + while (EOF != (ch = getc(fp))) { + if (dns_anyconf_match(pat, ch)) { + count++; + continue; + } + + ungetc(ch, fp); + + break; + } + + return count; +} /* dns_anyconf_skip() */ + + +static size_t dns_anyconf_scan(struct dns_anyconf *cf, const char *pat, FILE *fp, int *error) { + size_t len; + int ch; + + while (EOF != (ch = getc(fp))) { + if (dns_anyconf_match(pat, ch)) { + if ((*error = dns_anyconf_addc(cf, ch))) + return 0; + + continue; + } else { + ungetc(ch, fp); + + break; + } + } + + if ((len = cf->cp - cf->tp)) { + if ((*error = dns_anyconf_push(cf))) + return 0; + + return len; + } else { + *error = 0; + + return 0; + } +} /* dns_anyconf_scan() */ + + +DNS_NOTUSED static void dns_anyconf_dump(struct dns_anyconf *cf, FILE *fp) { + unsigned i; + + fprintf(fp, "tokens:"); + + for (i = 0; i < cf->count; i++) { + fprintf(fp, " %s", cf->token[i]); + } + + fputc('\n', fp); +} /* dns_anyconf_dump() */ + + +enum dns_nssconf_keyword { + DNS_NSSCONF_INVALID = 0, + DNS_NSSCONF_HOSTS = 1, + DNS_NSSCONF_SUCCESS, + DNS_NSSCONF_NOTFOUND, + DNS_NSSCONF_UNAVAIL, + DNS_NSSCONF_TRYAGAIN, + DNS_NSSCONF_CONTINUE, + DNS_NSSCONF_RETURN, + DNS_NSSCONF_FILES, + DNS_NSSCONF_DNS, + DNS_NSSCONF_MDNS, + + DNS_NSSCONF_LAST, +}; /* enum dns_nssconf_keyword */ + +static enum dns_nssconf_keyword dns_nssconf_keyword(const char *word) { + static const char *list[] = { + [DNS_NSSCONF_HOSTS] = "hosts", + [DNS_NSSCONF_SUCCESS] = "success", + [DNS_NSSCONF_NOTFOUND] = "notfound", + [DNS_NSSCONF_UNAVAIL] = "unavail", + [DNS_NSSCONF_TRYAGAIN] = "tryagain", + [DNS_NSSCONF_CONTINUE] = "continue", + [DNS_NSSCONF_RETURN] = "return", + [DNS_NSSCONF_FILES] = "files", + [DNS_NSSCONF_DNS] = "dns", + [DNS_NSSCONF_MDNS] = "mdns", + }; + unsigned i; + + for (i = 1; i < lengthof(list); i++) { + if (list[i] && 0 == strcasecmp(list[i], word)) + return i; + } + + return DNS_NSSCONF_INVALID; +} /* dns_nssconf_keyword() */ + + +static enum dns_nssconf_keyword dns_nssconf_c2k(int ch) { + static const char map[] = { + ['S'] = DNS_NSSCONF_SUCCESS, + ['N'] = DNS_NSSCONF_NOTFOUND, + ['U'] = DNS_NSSCONF_UNAVAIL, + ['T'] = DNS_NSSCONF_TRYAGAIN, + ['C'] = DNS_NSSCONF_CONTINUE, + ['R'] = DNS_NSSCONF_RETURN, + ['f'] = DNS_NSSCONF_FILES, + ['F'] = DNS_NSSCONF_FILES, + ['d'] = DNS_NSSCONF_DNS, + ['D'] = DNS_NSSCONF_DNS, + ['b'] = DNS_NSSCONF_DNS, + ['B'] = DNS_NSSCONF_DNS, + ['m'] = DNS_NSSCONF_MDNS, + ['M'] = DNS_NSSCONF_MDNS, + }; + + return (ch >= 0 && ch < (int)lengthof(map))? map[ch] : DNS_NSSCONF_INVALID; +} /* dns_nssconf_c2k() */ + + +DNS_PRAGMA_PUSH +DNS_PRAGMA_QUIET + +static int dns_nssconf_k2c(int k) { + static const char map[DNS_NSSCONF_LAST] = { + [DNS_NSSCONF_SUCCESS] = 'S', + [DNS_NSSCONF_NOTFOUND] = 'N', + [DNS_NSSCONF_UNAVAIL] = 'U', + [DNS_NSSCONF_TRYAGAIN] = 'T', + [DNS_NSSCONF_CONTINUE] = 'C', + [DNS_NSSCONF_RETURN] = 'R', + [DNS_NSSCONF_FILES] = 'f', + [DNS_NSSCONF_DNS] = 'b', + [DNS_NSSCONF_MDNS] = 'm', + }; + + return (k >= 0 && k < (int)lengthof(map))? (map[k]? map[k] : '?') : '?'; +} /* dns_nssconf_k2c() */ + +static const char *dns_nssconf_k2s(int k) { + static const char *const map[DNS_NSSCONF_LAST] = { + [DNS_NSSCONF_SUCCESS] = "SUCCESS", + [DNS_NSSCONF_NOTFOUND] = "NOTFOUND", + [DNS_NSSCONF_UNAVAIL] = "UNAVAIL", + [DNS_NSSCONF_TRYAGAIN] = "TRYAGAIN", + [DNS_NSSCONF_CONTINUE] = "continue", + [DNS_NSSCONF_RETURN] = "return", + [DNS_NSSCONF_FILES] = "files", + [DNS_NSSCONF_DNS] = "dns", + [DNS_NSSCONF_MDNS] = "mdns", + }; + + return (k >= 0 && k < (int)lengthof(map))? (map[k]? map[k] : "") : ""; +} /* dns_nssconf_k2s() */ + +DNS_PRAGMA_POP + + +int dns_nssconf_loadfile(struct dns_resolv_conf *resconf, FILE *fp) { + enum dns_nssconf_keyword source, status, action; + char lookup[sizeof resconf->lookup] = "", *lp; + struct dns_anyconf cf; + size_t i; + int error; + + while (!feof(fp) && !ferror(fp)) { + dns_anyconf_reset(&cf); + + dns_anyconf_skip("%s", fp); + + if (!dns_anyconf_scan(&cf, "%w_", fp, &error)) + goto nextent; + + if (DNS_NSSCONF_HOSTS != dns_nssconf_keyword(cf.token[0])) + goto nextent; + + dns_anyconf_pop(&cf); + + if (!dns_anyconf_skip(": \t", fp)) + goto nextent; + + *(lp = lookup) = '\0'; + + while (dns_anyconf_scan(&cf, "%w_", fp, &error)) { + dns_anyconf_skip(" \t", fp); + + if ('[' == dns_anyconf_peek(fp)) { + dns_anyconf_skip("[! \t", fp); + + while (dns_anyconf_scan(&cf, "%w_", fp, &error)) { + dns_anyconf_skip("= \t", fp); + if (!dns_anyconf_scan(&cf, "%w_", fp, &error)) { + dns_anyconf_pop(&cf); /* discard status */ + dns_anyconf_skip("^#;]\n", fp); /* skip to end of criteria */ + break; + } + dns_anyconf_skip(" \t", fp); + } + + dns_anyconf_skip("] \t", fp); + } + + if ((size_t)(endof(lookup) - lp) < cf.count + 1) /* +1 for '\0' */ + goto nextsrc; + + source = dns_nssconf_keyword(cf.token[0]); + + switch (source) { + case DNS_NSSCONF_DNS: + case DNS_NSSCONF_MDNS: + case DNS_NSSCONF_FILES: + *lp++ = dns_nssconf_k2c(source); + break; + default: + goto nextsrc; + } + + for (i = 1; i + 1 < cf.count; i += 2) { + status = dns_nssconf_keyword(cf.token[i]); + action = dns_nssconf_keyword(cf.token[i + 1]); + + switch (status) { + case DNS_NSSCONF_SUCCESS: + case DNS_NSSCONF_NOTFOUND: + case DNS_NSSCONF_UNAVAIL: + case DNS_NSSCONF_TRYAGAIN: + *lp++ = dns_nssconf_k2c(status); + break; + default: + continue; + } + + switch (action) { + case DNS_NSSCONF_CONTINUE: + case DNS_NSSCONF_RETURN: + break; + default: + action = (status == DNS_NSSCONF_SUCCESS) + ? DNS_NSSCONF_RETURN + : DNS_NSSCONF_CONTINUE; + break; + } + + *lp++ = dns_nssconf_k2c(action); + } +nextsrc: + *lp = '\0'; + dns_anyconf_reset(&cf); + } +nextent: + dns_anyconf_skip("^\n", fp); + } + + if (*lookup) + strncpy(resconf->lookup, lookup, sizeof resconf->lookup); + + return 0; +} /* dns_nssconf_loadfile() */ + + +int dns_nssconf_loadpath(struct dns_resolv_conf *resconf, const char *path) { + FILE *fp; + int error; + + if (!(fp = dns_fopen(path, "rt", &error))) + return error; + + error = dns_nssconf_loadfile(resconf, fp); + + fclose(fp); + + return error; +} /* dns_nssconf_loadpath() */ + + +struct dns_nssconf_source { + enum dns_nssconf_keyword source, success, notfound, unavail, tryagain; +}; /* struct dns_nssconf_source */ + +typedef unsigned dns_nssconf_i; + +static inline int dns_nssconf_peek(const struct dns_resolv_conf *resconf, dns_nssconf_i state) { + return (state < lengthof(resconf->lookup) && resconf->lookup[state])? resconf->lookup[state] : 0; +} /* dns_nssconf_peek() */ + +static _Bool dns_nssconf_next(struct dns_nssconf_source *src, const struct dns_resolv_conf *resconf, dns_nssconf_i *state) { + int source, status, action; + + src->source = DNS_NSSCONF_INVALID; + src->success = DNS_NSSCONF_RETURN; + src->notfound = DNS_NSSCONF_CONTINUE; + src->unavail = DNS_NSSCONF_CONTINUE; + src->tryagain = DNS_NSSCONF_CONTINUE; + + while ((source = dns_nssconf_peek(resconf, *state))) { + source = dns_nssconf_c2k(source); + ++*state; + + switch (source) { + case DNS_NSSCONF_FILES: + case DNS_NSSCONF_DNS: + case DNS_NSSCONF_MDNS: + src->source = source; + break; + default: + continue; + } + + while ((status = dns_nssconf_peek(resconf, *state)) && (action = dns_nssconf_peek(resconf, *state + 1))) { + status = dns_nssconf_c2k(status); + action = dns_nssconf_c2k(action); + + switch (action) { + case DNS_NSSCONF_RETURN: + case DNS_NSSCONF_CONTINUE: + break; + default: + goto done; + } + + switch (status) { + case DNS_NSSCONF_SUCCESS: + src->success = action; + break; + case DNS_NSSCONF_NOTFOUND: + src->notfound = action; + break; + case DNS_NSSCONF_UNAVAIL: + src->unavail = action; + break; + case DNS_NSSCONF_TRYAGAIN: + src->tryagain = action; + break; + default: + goto done; + } + + *state += 2; + } + + break; + } +done: + return src->source != DNS_NSSCONF_INVALID; +} /* dns_nssconf_next() */ + + +static int dns_nssconf_dump_status(int status, int action, unsigned *count, FILE *fp) { + switch (status) { + case DNS_NSSCONF_SUCCESS: + if (action == DNS_NSSCONF_RETURN) + return 0; + break; + default: + if (action == DNS_NSSCONF_CONTINUE) + return 0; + break; + } + + fputc(' ', fp); + + if (!*count) + fputc('[', fp); + + fprintf(fp, "%s=%s", dns_nssconf_k2s(status), dns_nssconf_k2s(action)); + + ++*count; + + return 0; +} /* dns_nssconf_dump_status() */ + + +int dns_nssconf_dump(struct dns_resolv_conf *resconf, FILE *fp) { + struct dns_nssconf_source src; + dns_nssconf_i i = 0; + + fputs("hosts:", fp); + + while (dns_nssconf_next(&src, resconf, &i)) { + unsigned n = 0; + + fprintf(fp, " %s", dns_nssconf_k2s(src.source)); + + dns_nssconf_dump_status(DNS_NSSCONF_SUCCESS, src.success, &n, fp); + dns_nssconf_dump_status(DNS_NSSCONF_NOTFOUND, src.notfound, &n, fp); + dns_nssconf_dump_status(DNS_NSSCONF_UNAVAIL, src.unavail, &n, fp); + dns_nssconf_dump_status(DNS_NSSCONF_TRYAGAIN, src.tryagain, &n, fp); + + if (n) + fputc(']', fp); + } + + fputc('\n', fp); + + return 0; +} /* dns_nssconf_dump() */ + + +int dns_resconf_setiface(struct dns_resolv_conf *resconf, const char *addr, unsigned short port) { + int af = (strchr(addr, ':'))? AF_INET6 : AF_INET; + int error; + + if ((error = dns_pton(af, addr, dns_sa_addr(af, &resconf->iface, NULL)))) + return error; + + *dns_sa_port(af, &resconf->iface) = htons(port); + resconf->iface.ss_family = af; + + return 0; +} /* dns_resconf_setiface() */ + + +#define DNS_SM_RESTORE \ + do { \ + pc = 0xff & (*state >> 0); \ + srchi = 0xff & (*state >> 8); \ + ndots = 0xff & (*state >> 16); \ + } while (0) + +#define DNS_SM_SAVE \ + do { \ + *state = ((0xff & pc) << 0) \ + | ((0xff & srchi) << 8) \ + | ((0xff & ndots) << 16); \ + } while (0) + +size_t dns_resconf_search(void *dst, size_t lim, const void *qname, size_t qlen, struct dns_resolv_conf *resconf, dns_resconf_i_t *state) { + unsigned pc, srchi, ndots, len; + + DNS_SM_ENTER; + + /* if FQDN then return as-is and finish */ + if (dns_d_isanchored(qname, qlen)) { + len = dns_d_anchor(dst, lim, qname, qlen); + DNS_SM_YIELD(len); + DNS_SM_EXIT; + } + + ndots = dns_d_ndots(qname, qlen); + + if (ndots >= resconf->options.ndots) { + len = dns_d_anchor(dst, lim, qname, qlen); + DNS_SM_YIELD(len); + } + + while (srchi < lengthof(resconf->search) && resconf->search[srchi][0]) { + struct dns_buf buf = DNS_B_INTO(dst, lim); + const char *dn = resconf->search[srchi++]; + + dns_b_put(&buf, qname, qlen); + dns_b_putc(&buf, '.'); + dns_b_puts(&buf, dn); + if (!dns_d_isanchored(dn, strlen(dn))) + dns_b_putc(&buf, '.'); + len = dns_b_strllen(&buf); + DNS_SM_YIELD(len); + } + + if (ndots < resconf->options.ndots) { + len = dns_d_anchor(dst, lim, qname, qlen); + DNS_SM_YIELD(len); + } + + DNS_SM_LEAVE; + + return dns_strlcpy(dst, "", lim); +} /* dns_resconf_search() */ + +#undef DNS_SM_SAVE +#undef DNS_SM_RESTORE + + +int dns_resconf_dump(struct dns_resolv_conf *resconf, FILE *fp) { + unsigned i; + int af; + + for (i = 0; i < lengthof(resconf->nameserver) && (af = resconf->nameserver[i].ss_family) != AF_UNSPEC; i++) { + char addr[INET6_ADDRSTRLEN + 1] = "[INVALID]"; + unsigned short port; + + dns_inet_ntop(af, dns_sa_addr(af, &resconf->nameserver[i], NULL), addr, sizeof addr); + port = ntohs(*dns_sa_port(af, &resconf->nameserver[i])); + + if (port == 53) + fprintf(fp, "nameserver %s\n", addr); + else + fprintf(fp, "nameserver [%s]:%hu\n", addr, port); + } + + + fprintf(fp, "search"); + + for (i = 0; i < lengthof(resconf->search) && resconf->search[i][0]; i++) + fprintf(fp, " %s", resconf->search[i]); + + fputc('\n', fp); + + + fputs("; ", fp); + dns_nssconf_dump(resconf, fp); + + fprintf(fp, "lookup"); + + for (i = 0; i < lengthof(resconf->lookup) && resconf->lookup[i]; i++) { + switch (resconf->lookup[i]) { + case 'b': + fprintf(fp, " bind"); break; + case 'f': + fprintf(fp, " file"); break; + case 'c': + fprintf(fp, " cache"); break; + } + } + + fputc('\n', fp); + + + fprintf(fp, "options ndots:%u timeout:%u attempts:%u", resconf->options.ndots, resconf->options.timeout, resconf->options.attempts); + + if (resconf->options.edns0) + fprintf(fp, " edns0"); + if (resconf->options.rotate) + fprintf(fp, " rotate"); + if (resconf->options.recurse) + fprintf(fp, " recurse"); + if (resconf->options.smart) + fprintf(fp, " smart"); + + switch (resconf->options.tcp) { + case DNS_RESCONF_TCP_ENABLE: + break; + case DNS_RESCONF_TCP_ONLY: + fprintf(fp, " tcp"); + break; + case DNS_RESCONF_TCP_DISABLE: + fprintf(fp, " tcp:disable"); + break; + } + + fputc('\n', fp); + + + if ((af = resconf->iface.ss_family) != AF_UNSPEC) { + char addr[INET6_ADDRSTRLEN + 1] = "[INVALID]"; + + dns_inet_ntop(af, dns_sa_addr(af, &resconf->iface, NULL), addr, sizeof addr); + + fprintf(fp, "interface %s %hu\n", addr, ntohs(*dns_sa_port(af, &resconf->iface))); + } + + return 0; +} /* dns_resconf_dump() */ + + +/* + * H I N T S E R V E R R O U T I N E S + * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +struct dns_hints_soa { + unsigned char zone[DNS_D_MAXNAME + 1]; + + struct { + struct sockaddr_storage ss; + unsigned priority; + } addrs[16]; + + unsigned count; + + struct dns_hints_soa *next; +}; /* struct dns_hints_soa */ + + +struct dns_hints { + dns_atomic_t refcount; + + struct dns_hints_soa *head; +}; /* struct dns_hints */ + + +struct dns_hints *dns_hints_open(struct dns_resolv_conf *resconf, int *error) { + static const struct dns_hints H_initializer; + struct dns_hints *H; + + (void)resconf; + + if (!(H = malloc(sizeof *H))) + goto syerr; + + *H = H_initializer; + + dns_hints_acquire(H); + + return H; +syerr: + *error = dns_syerr(); + + free(H); + + return 0; +} /* dns_hints_open() */ + + +void dns_hints_close(struct dns_hints *H) { + struct dns_hints_soa *soa, *nxt; + + if (!H || 1 != dns_hints_release(H)) + return /* void */; + + for (soa = H->head; soa; soa = nxt) { + nxt = soa->next; + + free(soa); + } + + free(H); + + return /* void */; +} /* dns_hints_close() */ + + +dns_refcount_t dns_hints_acquire(struct dns_hints *H) { + return dns_atomic_fetch_add(&H->refcount); +} /* dns_hints_acquire() */ + + +dns_refcount_t dns_hints_release(struct dns_hints *H) { + return dns_atomic_fetch_sub(&H->refcount); +} /* dns_hints_release() */ + + +struct dns_hints *dns_hints_mortal(struct dns_hints *hints) { + if (hints) + dns_hints_release(hints); + + return hints; +} /* dns_hints_mortal() */ + + +struct dns_hints *dns_hints_local(struct dns_resolv_conf *resconf, int *error_) { + struct dns_hints *hints = 0; + int error; + + if (resconf) + dns_resconf_acquire(resconf); + else if (!(resconf = dns_resconf_local(&error))) + goto error; + + if (!(hints = dns_hints_open(resconf, &error))) + goto error; + + error = 0; + + if (0 == dns_hints_insert_resconf(hints, ".", resconf, &error) && error) + goto error; + + dns_resconf_close(resconf); + + return hints; +error: + *error_ = error; + + dns_resconf_close(resconf); + dns_hints_close(hints); + + return 0; +} /* dns_hints_local() */ + + +struct dns_hints *dns_hints_root(struct dns_resolv_conf *resconf, int *error_) { + static const struct { + int af; + char addr[INET6_ADDRSTRLEN]; + } root_hints[] = { + { AF_INET, "198.41.0.4" }, /* A.ROOT-SERVERS.NET. */ + { AF_INET6, "2001:503:ba3e::2:30" }, /* A.ROOT-SERVERS.NET. */ + { AF_INET, "192.228.79.201" }, /* B.ROOT-SERVERS.NET. */ + { AF_INET6, "2001:500:84::b" }, /* B.ROOT-SERVERS.NET. */ + { AF_INET, "192.33.4.12" }, /* C.ROOT-SERVERS.NET. */ + { AF_INET6, "2001:500:2::c" }, /* C.ROOT-SERVERS.NET. */ + { AF_INET, "199.7.91.13" }, /* D.ROOT-SERVERS.NET. */ + { AF_INET6, "2001:500:2d::d" }, /* D.ROOT-SERVERS.NET. */ + { AF_INET, "192.203.230.10" }, /* E.ROOT-SERVERS.NET. */ + { AF_INET, "192.5.5.241" }, /* F.ROOT-SERVERS.NET. */ + { AF_INET6, "2001:500:2f::f" }, /* F.ROOT-SERVERS.NET. */ + { AF_INET, "192.112.36.4" }, /* G.ROOT-SERVERS.NET. */ + { AF_INET, "128.63.2.53" }, /* H.ROOT-SERVERS.NET. */ + { AF_INET6, "2001:500:1::803f:235" }, /* H.ROOT-SERVERS.NET. */ + { AF_INET, "192.36.148.17" }, /* I.ROOT-SERVERS.NET. */ + { AF_INET6, "2001:7FE::53" }, /* I.ROOT-SERVERS.NET. */ + { AF_INET, "192.58.128.30" }, /* J.ROOT-SERVERS.NET. */ + { AF_INET6, "2001:503:c27::2:30" }, /* J.ROOT-SERVERS.NET. */ + { AF_INET, "193.0.14.129" }, /* K.ROOT-SERVERS.NET. */ + { AF_INET6, "2001:7FD::1" }, /* K.ROOT-SERVERS.NET. */ + { AF_INET, "199.7.83.42" }, /* L.ROOT-SERVERS.NET. */ + { AF_INET6, "2001:500:3::42" }, /* L.ROOT-SERVERS.NET. */ + { AF_INET, "202.12.27.33" }, /* M.ROOT-SERVERS.NET. */ + { AF_INET6, "2001:DC3::35" }, /* M.ROOT-SERVERS.NET. */ + }; + struct dns_hints *hints = 0; + struct sockaddr_storage ss; + unsigned i; + int error, af; + + if (!(hints = dns_hints_open(resconf, &error))) + goto error; + + for (i = 0; i < lengthof(root_hints); i++) { + af = root_hints[i].af; + + if ((error = dns_pton(af, root_hints[i].addr, dns_sa_addr(af, &ss, NULL)))) + goto error; + + *dns_sa_port(af, &ss) = htons(53); + ss.ss_family = af; + + if ((error = dns_hints_insert(hints, ".", (struct sockaddr *)&ss, 1))) + goto error; + } + + return hints; +error: + *error_ = error; + + dns_hints_close(hints); + + return 0; +} /* dns_hints_root() */ + + +static struct dns_hints_soa *dns_hints_fetch(struct dns_hints *H, const char *zone) { + struct dns_hints_soa *soa; + + for (soa = H->head; soa; soa = soa->next) { + if (0 == strcasecmp(zone, (char *)soa->zone)) + return soa; + } + + return 0; +} /* dns_hints_fetch() */ + + +int dns_hints_insert(struct dns_hints *H, const char *zone, const struct sockaddr *sa, unsigned priority) { + static const struct dns_hints_soa soa_initializer; + struct dns_hints_soa *soa; + unsigned i; + + if (!(soa = dns_hints_fetch(H, zone))) { + if (!(soa = malloc(sizeof *soa))) + return dns_syerr(); + *soa = soa_initializer; + dns_strlcpy((char *)soa->zone, zone, sizeof soa->zone); + + soa->next = H->head; + H->head = soa; + } + + i = soa->count % lengthof(soa->addrs); + + memcpy(&soa->addrs[i].ss, sa, dns_sa_len(sa)); + + soa->addrs[i].priority = DNS_PP_MAX(1, priority); + + if (soa->count < lengthof(soa->addrs)) + soa->count++; + + return 0; +} /* dns_hints_insert() */ + + +static _Bool dns_hints_isinaddr_any(const void *sa) { + struct in_addr *addr; + + if (dns_sa_family(sa) != AF_INET) + return 0; + + addr = dns_sa_addr(AF_INET, sa, NULL); + return addr->s_addr == htonl(INADDR_ANY); +} + +unsigned dns_hints_insert_resconf(struct dns_hints *H, const char *zone, const struct dns_resolv_conf *resconf, int *error_) { + unsigned i, n, p; + int error; + + for (i = 0, n = 0, p = 1; i < lengthof(resconf->nameserver) && resconf->nameserver[i].ss_family != AF_UNSPEC; i++, n++) { + union { struct sockaddr_in sin; } tmp; + struct sockaddr *ns; + + /* + * dns_resconf_open initializes nameserver[0] to INADDR_ANY. + * + * Traditionally the semantics of 0.0.0.0 meant the default + * interface, which evolved to mean the loopback interface. + * See comment block preceding resolv/res_init.c:res_init in + * glibc 2.23. As of 2.23, glibc no longer translates + * 0.0.0.0 despite the code comment, but it does default to + * 127.0.0.1 when no nameservers are present. + * + * BIND9 as of 9.10.3 still translates 0.0.0.0 to 127.0.0.1. + * See lib/lwres/lwconfig.c:lwres_create_addr and the + * convert_zero flag. 127.0.0.1 is also the default when no + * nameservers are present. + */ + if (dns_hints_isinaddr_any(&resconf->nameserver[i])) { + memcpy(&tmp.sin, &resconf->nameserver[i], sizeof tmp.sin); + tmp.sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + ns = (struct sockaddr *)&tmp.sin; + } else { + ns = (struct sockaddr *)&resconf->nameserver[i]; + } + + if ((error = dns_hints_insert(H, zone, ns, p))) + goto error; + + p += !resconf->options.rotate; + } + + return n; +error: + *error_ = error; + + return n; +} /* dns_hints_insert_resconf() */ + + +static int dns_hints_i_cmp(unsigned a, unsigned b, struct dns_hints_i *i, struct dns_hints_soa *soa) { + int cmp; + + if ((cmp = soa->addrs[a].priority - soa->addrs[b].priority)) + return cmp; + + return dns_k_shuffle16(a, i->state.seed) - dns_k_shuffle16(b, i->state.seed); +} /* dns_hints_i_cmp() */ + + +static unsigned dns_hints_i_start(struct dns_hints_i *i, struct dns_hints_soa *soa) { + unsigned p0, p; + + p0 = 0; + + for (p = 1; p < soa->count; p++) { + if (dns_hints_i_cmp(p, p0, i, soa) < 0) + p0 = p; + } + + return p0; +} /* dns_hints_i_start() */ + + +static unsigned dns_hints_i_skip(unsigned p0, struct dns_hints_i *i, struct dns_hints_soa *soa) { + unsigned pZ, p; + + for (pZ = 0; pZ < soa->count; pZ++) { + if (dns_hints_i_cmp(pZ, p0, i, soa) > 0) + goto cont; + } + + return soa->count; +cont: + for (p = pZ + 1; p < soa->count; p++) { + if (dns_hints_i_cmp(p, p0, i, soa) <= 0) + continue; + + if (dns_hints_i_cmp(p, pZ, i, soa) >= 0) + continue; + + pZ = p; + } + + + return pZ; +} /* dns_hints_i_skip() */ + + +static struct dns_hints_i *dns_hints_i_init(struct dns_hints_i *i, struct dns_hints *hints) { + static const struct dns_hints_i i_initializer; + struct dns_hints_soa *soa; + + i->state = i_initializer.state; + + do { + i->state.seed = dns_random(); + } while (0 == i->state.seed); + + if ((soa = dns_hints_fetch(hints, i->zone))) { + i->state.next = dns_hints_i_start(i, soa); + } + + return i; +} /* dns_hints_i_init() */ + + +unsigned dns_hints_grep(struct sockaddr **sa, socklen_t *sa_len, unsigned lim, struct dns_hints_i *i, struct dns_hints *H) { + struct dns_hints_soa *soa; + unsigned n; + + if (!(soa = dns_hints_fetch(H, i->zone))) + return 0; + + n = 0; + + while (i->state.next < soa->count && n < lim) { + *sa = (struct sockaddr *)&soa->addrs[i->state.next].ss; + *sa_len = dns_sa_len(*sa); + + sa++; + sa_len++; + n++; + + i->state.next = dns_hints_i_skip(i->state.next, i, soa); + } + + return n; +} /* dns_hints_grep() */ + + +struct dns_packet *dns_hints_query(struct dns_hints *hints, struct dns_packet *Q, int *error_) { + struct dns_packet *A, *P; + struct dns_rr rr; + char zone[DNS_D_MAXNAME + 1]; + size_t zlen; + struct dns_hints_i i; + struct sockaddr *sa; + socklen_t slen; + int error; + + if (!dns_rr_grep(&rr, 1, dns_rr_i_new(Q, .section = DNS_S_QUESTION), Q, &error)) + goto error; + + if (!(zlen = dns_d_expand(zone, sizeof zone, rr.dn.p, Q, &error))) + goto error; + else if (zlen >= sizeof zone) + goto toolong; + + P = dns_p_new(512); + dns_header(P)->qr = 1; + + if ((error = dns_rr_copy(P, &rr, Q))) + goto error; + + if ((error = dns_p_push(P, DNS_S_AUTHORITY, ".", strlen("."), DNS_T_NS, DNS_C_IN, 0, "hints.local."))) + goto error; + + do { + i.zone = zone; + + dns_hints_i_init(&i, hints); + + while (dns_hints_grep(&sa, &slen, 1, &i, hints)) { + int af = sa->sa_family; + int rtype = (af == AF_INET6)? DNS_T_AAAA : DNS_T_A; + + if ((error = dns_p_push(P, DNS_S_ADDITIONAL, "hints.local.", strlen("hints.local."), rtype, DNS_C_IN, 0, dns_sa_addr(af, sa, NULL)))) + goto error; + } + } while ((zlen = dns_d_cleave(zone, sizeof zone, zone, zlen))); + + if (!(A = dns_p_copy(dns_p_make(P->end, &error), P))) + goto error; + + return A; +toolong: + error = DNS_EILLEGAL; +error: + *error_ = error; + + return 0; +} /* dns_hints_query() */ + + +/** ugly hack to support specifying ports other than 53 in resolv.conf. */ +static unsigned short dns_hints_port(struct dns_hints *hints, int af, void *addr) { + struct dns_hints_soa *soa; + void *addrsoa; + socklen_t addrlen; + unsigned short port; + unsigned i; + + for (soa = hints->head; soa; soa = soa->next) { + for (i = 0; i < soa->count; i++) { + if (af != soa->addrs[i].ss.ss_family) + continue; + + if (!(addrsoa = dns_sa_addr(af, &soa->addrs[i].ss, &addrlen))) + continue; + + if (memcmp(addr, addrsoa, addrlen)) + continue; + + port = *dns_sa_port(af, &soa->addrs[i].ss); + + return (port)? port : htons(53); + } + } + + return htons(53); +} /* dns_hints_port() */ + + +int dns_hints_dump(struct dns_hints *hints, FILE *fp) { + struct dns_hints_soa *soa; + char addr[INET6_ADDRSTRLEN]; + unsigned i; + int af, error; + + for (soa = hints->head; soa; soa = soa->next) { + fprintf(fp, "ZONE \"%s\"\n", soa->zone); + + for (i = 0; i < soa->count; i++) { + af = soa->addrs[i].ss.ss_family; + + if ((error = dns_ntop(af, dns_sa_addr(af, &soa->addrs[i].ss, NULL), addr, sizeof addr))) + return error; + + fprintf(fp, "\t(%d) [%s]:%hu\n", (int)soa->addrs[i].priority, addr, ntohs(*dns_sa_port(af, &soa->addrs[i].ss))); + } + } + + return 0; +} /* dns_hints_dump() */ + + +/* + * C A C H E R O U T I N E S + * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +static dns_refcount_t dns_cache_acquire(struct dns_cache *cache) { + return dns_atomic_fetch_add(&cache->_.refcount); +} /* dns_cache_acquire() */ + + +static dns_refcount_t dns_cache_release(struct dns_cache *cache) { + return dns_atomic_fetch_sub(&cache->_.refcount); +} /* dns_cache_release() */ + + +static struct dns_packet *dns_cache_query(struct dns_packet *query, struct dns_cache *cache, int *error) { + (void)query; + (void)cache; + (void)error; + + return NULL; +} /* dns_cache_query() */ + + +static int dns_cache_submit(struct dns_packet *query, struct dns_cache *cache) { + (void)query; + (void)cache; + + return 0; +} /* dns_cache_submit() */ + + +static int dns_cache_check(struct dns_cache *cache) { + (void)cache; + + return 0; +} /* dns_cache_check() */ + + +static struct dns_packet *dns_cache_fetch(struct dns_cache *cache, int *error) { + (void)cache; + (void)error; + + return NULL; +} /* dns_cache_fetch() */ + + +static int dns_cache_pollfd(struct dns_cache *cache) { + (void)cache; + + return -1; +} /* dns_cache_pollfd() */ + + +static short dns_cache_events(struct dns_cache *cache) { + (void)cache; + + return 0; +} /* dns_cache_events() */ + + +static void dns_cache_clear(struct dns_cache *cache) { + (void)cache; + + return; +} /* dns_cache_clear() */ + + +struct dns_cache *dns_cache_init(struct dns_cache *cache) { + static const struct dns_cache c_init = { + .acquire = &dns_cache_acquire, + .release = &dns_cache_release, + .query = &dns_cache_query, + .submit = &dns_cache_submit, + .check = &dns_cache_check, + .fetch = &dns_cache_fetch, + .pollfd = &dns_cache_pollfd, + .events = &dns_cache_events, + .clear = &dns_cache_clear, + ._ = { .refcount = 1, }, + }; + + *cache = c_init; + + return cache; +} /* dns_cache_init() */ + + +void dns_cache_close(struct dns_cache *cache) { + if (cache) + cache->release(cache); +} /* dns_cache_close() */ + + +/* + * S O C K E T R O U T I N E S + * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +static void dns_socketclose(int *fd, const struct dns_options *opts) { + if (opts && opts->closefd.cb) + opts->closefd.cb(fd, opts->closefd.arg); + + if (*fd != -1) { +#if _WIN32 + closesocket(*fd); +#else + close(*fd); +#endif + *fd = -1; + } +} /* dns_socketclose() */ + + +#ifndef HAVE_IOCTLSOCKET +#define HAVE_IOCTLSOCKET (_WIN32 || _WIN64) +#endif + +#ifndef HAVE_SOCK_CLOEXEC +#define HAVE_SOCK_CLOEXEC (defined SOCK_CLOEXEC) +#endif + +#ifndef HAVE_SOCK_NONBLOCK +#define HAVE_SOCK_NONBLOCK (defined SOCK_NONBLOCK) +#endif + +#define DNS_SO_MAXTRY 7 + +static int dns_socket(struct sockaddr *local, int type, int *error_) { + int fd = -1, flags, error; +#if defined FIONBIO + unsigned long opt; +#endif + + flags = 0; +#if HAVE_SOCK_CLOEXEC + flags |= SOCK_CLOEXEC; +#endif +#if HAVE_SOCK_NONBLOCK + flags |= SOCK_NONBLOCK; +#endif + if (-1 == (fd = socket(local->sa_family, type|flags, 0))) + goto soerr; + +#if defined F_SETFD && !HAVE_SOCK_CLOEXEC + if (-1 == fcntl(fd, F_SETFD, 1)) + goto syerr; +#endif + +#if defined O_NONBLOCK && !HAVE_SOCK_NONBLOCK + if (-1 == (flags = fcntl(fd, F_GETFL))) + goto syerr; + if (-1 == fcntl(fd, F_SETFL, flags | O_NONBLOCK)) + goto syerr; +#elif defined FIONBIO && HAVE_IOCTLSOCKET + opt = 1; + if (0 != ioctlsocket(fd, FIONBIO, &opt)) + goto soerr; +#endif + +#if defined SO_NOSIGPIPE + if (type != SOCK_DGRAM) { + if (0 != setsockopt(fd, SOL_SOCKET, SO_NOSIGPIPE, &(int){ 1 }, sizeof (int))) + goto soerr; + } +#endif + + if (local->sa_family != AF_INET && local->sa_family != AF_INET6) + return fd; + + if (type != SOCK_DGRAM) + return fd; + + /* + * FreeBSD, Linux, OpenBSD, OS X, and Solaris use random ports by + * default. Though the ephemeral range is quite small on OS X + * (49152-65535 on 10.10) and Linux (32768-60999 on 4.4.0, Ubuntu + * Xenial). See also RFC 6056. + * + * TODO: Optionally rely on the kernel to select a random port. + */ + if (*dns_sa_port(local->sa_family, local) == 0) { + struct sockaddr_storage tmp; + unsigned i, port; + + memcpy(&tmp, local, dns_sa_len(local)); + + for (i = 0; i < DNS_SO_MAXTRY; i++) { + port = 1025 + (dns_random() % 64510); + + *dns_sa_port(tmp.ss_family, &tmp) = htons(port); + + if (0 == bind(fd, (struct sockaddr *)&tmp, dns_sa_len(&tmp))) + return fd; + } + + /* NB: continue to next bind statement */ + } + + if (0 == bind(fd, local, dns_sa_len(local))) + return fd; + + /* FALL THROUGH */ +soerr: + error = dns_soerr(); + + goto error; +#if (defined F_SETFD && !HAVE_SOCK_CLOEXEC) || (defined O_NONBLOCK && !HAVE_SOCK_NONBLOCK) +syerr: + error = dns_syerr(); + + goto error; +#endif +error: + *error_ = error; + + dns_socketclose(&fd, NULL); + + return -1; +} /* dns_socket() */ + + +enum { + DNS_SO_UDP_INIT = 1, + DNS_SO_UDP_CONN, + DNS_SO_UDP_SEND, + DNS_SO_UDP_RECV, + DNS_SO_UDP_DONE, + + DNS_SO_TCP_INIT, + DNS_SO_TCP_CONN, + DNS_SO_TCP_SEND, + DNS_SO_TCP_RECV, + DNS_SO_TCP_DONE, +}; + +struct dns_socket { + struct dns_options opts; + + int udp; + int tcp; + + int *old; + unsigned onum, olim; + + int type; + + struct sockaddr_storage local, remote; + + struct dns_k_permutor qids; + + struct dns_stat stat; + + /* + * NOTE: dns_so_reset() zeroes everything from here down. + */ + int state; + + unsigned short qid; + char qname[DNS_D_MAXNAME + 1]; + size_t qlen; + enum dns_type qtype; + enum dns_class qclass; + + struct dns_packet *query; + size_t qout; + + struct dns_clock elapsed; + + struct dns_packet *answer; + size_t alen, apos; +}; /* struct dns_socket */ + + +/* + * NOTE: Actual closure delayed so that kqueue(2) and epoll(2) callers have + * a chance to recognize a state change after installing a persistent event + * and where sequential descriptors with the same integer value returned + * from _pollfd() would be ambiguous. See dns_so_closefds(). + */ +static int dns_so_closefd(struct dns_socket *so, int *fd) { + int error; + + if (*fd == -1) + return 0; + + if (so->opts.closefd.cb) { + if ((error = so->opts.closefd.cb(fd, so->opts.closefd.arg))) { + return error; + } else if (*fd == -1) + return 0; + } + + if (!(so->onum < so->olim)) { + unsigned olim = DNS_PP_MAX(4, so->olim * 2); + void *old; + + if (!(old = realloc(so->old, sizeof so->old[0] * olim))) + return dns_syerr(); + + so->old = old; + so->olim = olim; + } + + so->old[so->onum++] = *fd; + *fd = -1; + + return 0; +} /* dns_so_closefd() */ + + +#define DNS_SO_CLOSE_UDP 0x01 +#define DNS_SO_CLOSE_TCP 0x02 +#define DNS_SO_CLOSE_OLD 0x04 +#define DNS_SO_CLOSE_ALL (DNS_SO_CLOSE_UDP|DNS_SO_CLOSE_TCP|DNS_SO_CLOSE_OLD) + +static void dns_so_closefds(struct dns_socket *so, int which) { + if (DNS_SO_CLOSE_UDP & which) + dns_socketclose(&so->udp, &so->opts); + if (DNS_SO_CLOSE_TCP & which) + dns_socketclose(&so->tcp, &so->opts); + if (DNS_SO_CLOSE_OLD & which) { + unsigned i; + for (i = 0; i < so->onum; i++) + dns_socketclose(&so->old[i], &so->opts); + so->onum = 0; + free(so->old); + so->old = 0; + so->olim = 0; + } +} /* dns_so_closefds() */ + + +static void dns_so_destroy(struct dns_socket *); + +static struct dns_socket *dns_so_init(struct dns_socket *so, const struct sockaddr *local, int type, const struct dns_options *opts, int *error) { + static const struct dns_socket so_initializer = { .opts = DNS_OPTS_INITIALIZER, .udp = -1, .tcp = -1, }; + + *so = so_initializer; + so->type = type; + + if (opts) + so->opts = *opts; + + if (local) + memcpy(&so->local, local, dns_sa_len(local)); + + if (-1 == (so->udp = dns_socket((struct sockaddr *)&so->local, SOCK_DGRAM, error))) + goto error; + + dns_k_permutor_init(&so->qids, 1, 65535); + + return so; +error: + dns_so_destroy(so); + + return 0; +} /* dns_so_init() */ + + +struct dns_socket *dns_so_open(const struct sockaddr *local, int type, const struct dns_options *opts, int *error) { + struct dns_socket *so; + + if (!(so = malloc(sizeof *so))) + goto syerr; + + if (!dns_so_init(so, local, type, opts, error)) + goto error; + + return so; +syerr: + *error = dns_syerr(); +error: + dns_so_close(so); + + return 0; +} /* dns_so_open() */ + + +static void dns_so_destroy(struct dns_socket *so) { + dns_so_reset(so); + dns_so_closefds(so, DNS_SO_CLOSE_ALL); +} /* dns_so_destroy() */ + + +void dns_so_close(struct dns_socket *so) { + if (!so) + return; + + dns_so_destroy(so); + + free(so); +} /* dns_so_close() */ + + +void dns_so_reset(struct dns_socket *so) { + dns_p_setptr(&so->answer, NULL); + + memset(&so->state, '\0', sizeof *so - offsetof(struct dns_socket, state)); +} /* dns_so_reset() */ + + +unsigned short dns_so_mkqid(struct dns_socket *so) { + return dns_k_permutor_step(&so->qids); +} /* dns_so_mkqid() */ + + +#define DNS_SO_MINBUF 768 + +static int dns_so_newanswer(struct dns_socket *so, size_t len) { + size_t size = offsetof(struct dns_packet, data) + DNS_PP_MAX(len, DNS_SO_MINBUF); + void *p; + + if (!(p = realloc(so->answer, size))) + return dns_syerr(); + + so->answer = dns_p_init(p, size); + + return 0; +} /* dns_so_newanswer() */ + + +int dns_so_submit(struct dns_socket *so, struct dns_packet *Q, struct sockaddr *host) { + struct dns_rr rr; + int error = DNS_EUNKNOWN; + + dns_so_reset(so); + + if ((error = dns_rr_parse(&rr, 12, Q))) + goto error; + + if (!(so->qlen = dns_d_expand(so->qname, sizeof so->qname, rr.dn.p, Q, &error))) + goto error; + /* + * NOTE: Don't bail if expansion is too long; caller may be + * intentionally sending long names. However, we won't be able to + * verify it on return. + */ + + so->qtype = rr.type; + so->qclass = rr.class; + + if ((error = dns_so_newanswer(so, (Q->memo.opt.maxudp)? Q->memo.opt.maxudp : DNS_SO_MINBUF))) + goto syerr; + + memcpy(&so->remote, host, dns_sa_len(host)); + + so->query = Q; + so->qout = 0; + + dns_begin(&so->elapsed); + + if (dns_header(so->query)->qid == 0) + dns_header(so->query)->qid = dns_so_mkqid(so); + + so->qid = dns_header(so->query)->qid; + so->state = (so->type == SOCK_STREAM)? DNS_SO_TCP_INIT : DNS_SO_UDP_INIT; + + so->stat.queries++; + + return 0; +syerr: + error = dns_syerr(); +error: + dns_so_reset(so); + + return error; +} /* dns_so_submit() */ + + +static int dns_so_verify(struct dns_socket *so, struct dns_packet *P) { + char qname[DNS_D_MAXNAME + 1]; + size_t qlen; + struct dns_rr rr; + int error = -1; + + if (so->qid != dns_header(so->answer)->qid) + goto reject; + + if (!dns_p_count(so->answer, DNS_S_QD)) + goto reject; + + if (0 != dns_rr_parse(&rr, 12, so->answer)) + goto reject; + + if (rr.type != so->qtype || rr.class != so->qclass) + goto reject; + + if (!(qlen = dns_d_expand(qname, sizeof qname, rr.dn.p, P, &error))) + goto error; + else if (qlen >= sizeof qname || qlen != so->qlen) + goto reject; + + if (0 != strcasecmp(so->qname, qname)) + goto reject; + + return 0; +reject: + error = DNS_EUNKNOWN; +error: + DNS_SHOW(P, "rejecting packet (%s)", dns_strerror(error)); + + return error; +} /* dns_so_verify() */ + + +static _Bool dns_so_tcp_keep(struct dns_socket *so) { + struct sockaddr_storage remote; + + if (so->tcp == -1) + return 0; + + if (0 != getpeername(so->tcp, (struct sockaddr *)&remote, &(socklen_t){ sizeof remote })) + return 0; + + return 0 == dns_sa_cmp(&remote, &so->remote); +} /* dns_so_tcp_keep() */ + + +#if defined __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Warray-bounds" +#endif + +static int dns_so_tcp_send(struct dns_socket *so) { + unsigned char *qsrc; + size_t qend; + long n; + + so->query->data[-2] = 0xff & (so->query->end >> 8); + so->query->data[-1] = 0xff & (so->query->end >> 0); + + qsrc = &so->query->data[-2] + so->qout; + qend = so->query->end + 2; + + while (so->qout < qend) { + if (0 > (n = dns_send(so->tcp, (void *)&qsrc[so->qout], qend - so->qout, 0))) + return dns_soerr(); + + so->qout += n; + so->stat.tcp.sent.bytes += n; + } + + so->stat.tcp.sent.count++; + + return 0; +} /* dns_so_tcp_send() */ + + +static int dns_so_tcp_recv(struct dns_socket *so) { + unsigned char *asrc; + size_t aend, alen; + int error; + long n; + + aend = so->alen + 2; + + while (so->apos < aend) { + asrc = &so->answer->data[-2]; + + if (0 > (n = recv(so->tcp, (void *)&asrc[so->apos], aend - so->apos, 0))) + return dns_soerr(); + else if (n == 0) + return DNS_EUNKNOWN; /* FIXME */ + + so->apos += n; + so->stat.tcp.rcvd.bytes += n; + + if (so->alen == 0 && so->apos >= 2) { + alen = ((0xff & so->answer->data[-2]) << 8) + | ((0xff & so->answer->data[-1]) << 0); + + if ((error = dns_so_newanswer(so, alen))) + return error; + + so->alen = alen; + aend = alen + 2; + } + } + + so->answer->end = so->alen; + so->stat.tcp.rcvd.count++; + + return 0; +} /* dns_so_tcp_recv() */ + +#if __clang__ +#pragma clang diagnostic pop +#endif + + +int dns_so_check(struct dns_socket *so) { + int error; + long n; + +retry: + switch (so->state) { + case DNS_SO_UDP_INIT: + so->state++; + case DNS_SO_UDP_CONN: + if (0 != connect(so->udp, (struct sockaddr *)&so->remote, dns_sa_len(&so->remote))) + goto soerr; + + so->state++; + case DNS_SO_UDP_SEND: + if (0 > (n = send(so->udp, (void *)so->query->data, so->query->end, 0))) + goto soerr; + + so->stat.udp.sent.bytes += n; + so->stat.udp.sent.count++; + + so->state++; + case DNS_SO_UDP_RECV: + if (0 > (n = recv(so->udp, (void *)so->answer->data, so->answer->size, 0))) + goto soerr; + + so->stat.udp.rcvd.bytes += n; + so->stat.udp.rcvd.count++; + + if ((so->answer->end = n) < 12) + goto trash; + + if ((error = dns_so_verify(so, so->answer))) + goto trash; + + so->state++; + case DNS_SO_UDP_DONE: + if (!dns_header(so->answer)->tc || so->type == SOCK_DGRAM) + return 0; + + so->state++; + case DNS_SO_TCP_INIT: + if (dns_so_tcp_keep(so)) { + so->state = DNS_SO_TCP_SEND; + + goto retry; + } + + if ((error = dns_so_closefd(so, &so->tcp))) + goto error; + + if (-1 == (so->tcp = dns_socket((struct sockaddr *)&so->local, SOCK_STREAM, &error))) + goto error; + + so->state++; + case DNS_SO_TCP_CONN: + if (0 != connect(so->tcp, (struct sockaddr *)&so->remote, dns_sa_len(&so->remote))) { + if (dns_soerr() != DNS_EISCONN) + goto soerr; + } + + so->state++; + case DNS_SO_TCP_SEND: + if ((error = dns_so_tcp_send(so))) + goto error; + + so->state++; + case DNS_SO_TCP_RECV: + if ((error = dns_so_tcp_recv(so))) + goto error; + + so->state++; + case DNS_SO_TCP_DONE: + /* close unless DNS_RESCONF_TCP_ONLY (see dns_res_tcp2type) */ + if (so->type != SOCK_STREAM) { + if ((error = dns_so_closefd(so, &so->tcp))) + goto error; + } + + if (so->answer->end < 12) + return DNS_EILLEGAL; + + if ((error = dns_so_verify(so, so->answer))) + goto error; + + return 0; + default: + error = DNS_EUNKNOWN; + + goto error; + } /* switch() */ + +trash: + DNS_CARP("discarding packet"); + goto retry; +soerr: + error = dns_soerr(); + + goto error; +error: + switch (error) { + case DNS_EINTR: + goto retry; + case DNS_EINPROGRESS: + /* FALL THROUGH */ + case DNS_EALREADY: + /* FALL THROUGH */ +#if DNS_EWOULDBLOCK != DNS_EAGAIN + case DNS_EWOULDBLOCK: + /* FALL THROUGH */ +#endif + error = DNS_EAGAIN; + + break; + } /* switch() */ + + return error; +} /* dns_so_check() */ + + +struct dns_packet *dns_so_fetch(struct dns_socket *so, int *error) { + struct dns_packet *answer; + + switch (so->state) { + case DNS_SO_UDP_DONE: + case DNS_SO_TCP_DONE: + answer = so->answer; + so->answer = 0; + + return answer; + default: + *error = DNS_EUNKNOWN; + + return 0; + } +} /* dns_so_fetch() */ + + +struct dns_packet *dns_so_query(struct dns_socket *so, struct dns_packet *Q, struct sockaddr *host, int *error_) { + struct dns_packet *A; + int error; + + if (!so->state) { + if ((error = dns_so_submit(so, Q, host))) + goto error; + } + + if ((error = dns_so_check(so))) + goto error; + + if (!(A = dns_so_fetch(so, &error))) + goto error; + + dns_so_reset(so); + + return A; +error: + *error_ = error; + + return 0; +} /* dns_so_query() */ + + +time_t dns_so_elapsed(struct dns_socket *so) { + return dns_elapsed(&so->elapsed); +} /* dns_so_elapsed() */ + + +void dns_so_clear(struct dns_socket *so) { + dns_so_closefds(so, DNS_SO_CLOSE_OLD); +} /* dns_so_clear() */ + + +static int dns_so_events2(struct dns_socket *so, enum dns_events type) { + int events = 0; + + switch (so->state) { + case DNS_SO_UDP_CONN: + case DNS_SO_UDP_SEND: + events |= DNS_POLLOUT; + + break; + case DNS_SO_UDP_RECV: + events |= DNS_POLLIN; + + break; + case DNS_SO_TCP_CONN: + case DNS_SO_TCP_SEND: + events |= DNS_POLLOUT; + + break; + case DNS_SO_TCP_RECV: + events |= DNS_POLLIN; + + break; + } /* switch() */ + + switch (type) { + case DNS_LIBEVENT: + return DNS_POLL2EV(events); + default: + return events; + } /* switch() */ +} /* dns_so_events2() */ + + +int dns_so_events(struct dns_socket *so) { + return dns_so_events2(so, so->opts.events); +} /* dns_so_events() */ + + +int dns_so_pollfd(struct dns_socket *so) { + switch (so->state) { + case DNS_SO_UDP_CONN: + case DNS_SO_UDP_SEND: + case DNS_SO_UDP_RECV: + return so->udp; + case DNS_SO_TCP_CONN: + case DNS_SO_TCP_SEND: + case DNS_SO_TCP_RECV: + return so->tcp; + } /* switch() */ + + return -1; +} /* dns_so_pollfd() */ + + +int dns_so_poll(struct dns_socket *so, int timeout) { + return dns_poll(dns_so_pollfd(so), dns_so_events2(so, DNS_SYSPOLL), timeout); +} /* dns_so_poll() */ + + +const struct dns_stat *dns_so_stat(struct dns_socket *so) { + return &so->stat; +} /* dns_so_stat() */ + + +/* + * R E S O L V E R R O U T I N E S + * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +enum dns_res_state { + DNS_R_INIT, + DNS_R_GLUE, + DNS_R_SWITCH, /* (B)IND, (F)ILE, (C)ACHE */ + + DNS_R_FILE, /* Lookup in local hosts database */ + + DNS_R_CACHE, /* Lookup in application cache */ + DNS_R_SUBMIT, + DNS_R_CHECK, + DNS_R_FETCH, + + DNS_R_BIND, /* Lookup in the network */ + DNS_R_SEARCH, + DNS_R_HINTS, + DNS_R_ITERATE, + DNS_R_FOREACH_NS, + DNS_R_RESOLV0_NS, /* Prologue: Setup next frame and recurse */ + DNS_R_RESOLV1_NS, /* Epilog: Inspect answer */ + DNS_R_FOREACH_A, + DNS_R_QUERY_A, + DNS_R_CNAME0_A, + DNS_R_CNAME1_A, + + DNS_R_FINISH, + DNS_R_SMART0_A, + DNS_R_SMART1_A, + DNS_R_DONE, + DNS_R_SERVFAIL, +}; /* enum dns_res_state */ + + +#define DNS_R_MAXDEPTH 8 +#define DNS_R_ENDFRAME (DNS_R_MAXDEPTH - 1) + +struct dns_resolver { + struct dns_socket so; + + struct dns_resolv_conf *resconf; + struct dns_hosts *hosts; + struct dns_hints *hints; + struct dns_cache *cache; + + dns_atomic_t refcount; + + /* Reset zeroes everything below here. */ + + char qname[DNS_D_MAXNAME + 1]; + size_t qlen; + + enum dns_type qtype; + enum dns_class qclass; + + struct dns_clock elapsed; + + dns_resconf_i_t search; + + struct dns_rr_i smart; + + struct dns_packet *nodata; /* answer if nothing better */ + + unsigned sp; + + struct dns_res_frame { + enum dns_res_state state; + + int error; + int which; /* (B)IND, (F)ILE; index into resconf->lookup */ + int qflags; + + unsigned attempts; + + struct dns_packet *query, *answer, *hints; + + struct dns_rr_i hints_i, hints_j; + struct dns_rr hints_ns, ans_cname; + } stack[DNS_R_MAXDEPTH]; +}; /* struct dns_resolver */ + + +static int dns_res_tcp2type(int tcp) { + switch (tcp) { + case DNS_RESCONF_TCP_ONLY: + return SOCK_STREAM; + case DNS_RESCONF_TCP_DISABLE: + return SOCK_DGRAM; + default: + return 0; + } +} /* dns_res_tcp2type() */ + +struct dns_resolver *dns_res_open(struct dns_resolv_conf *resconf, struct dns_hosts *hosts, struct dns_hints *hints, struct dns_cache *cache, const struct dns_options *opts, int *_error) { + static const struct dns_resolver R_initializer + = { .refcount = 1, }; + struct dns_resolver *R = 0; + int type, error; + + /* + * Grab ref count early because the caller may have passed us a mortal + * reference, and we want to do the right thing if we return early + * from an error. + */ + if (resconf) + dns_resconf_acquire(resconf); + if (hosts) + dns_hosts_acquire(hosts); + if (hints) + dns_hints_acquire(hints); + if (cache) + dns_cache_acquire(cache); + + /* + * Don't try to load it ourselves because a NULL object might be an + * error from, say, dns_resconf_root(), and loading + * dns_resconf_local() by default would create undesirable surpises. + */ + if (!resconf || !hosts || !hints) { + if (!*_error) + *_error = EINVAL; + goto _error; + } + + if (!(R = malloc(sizeof *R))) + goto syerr; + + *R = R_initializer; + type = dns_res_tcp2type(resconf->options.tcp); + + if (!dns_so_init(&R->so, (struct sockaddr *)&resconf->iface, type, opts, &error)) + goto error; + + R->resconf = resconf; + R->hosts = hosts; + R->hints = hints; + R->cache = cache; + + return R; +syerr: + error = dns_syerr(); +error: + *_error = error; +_error: + dns_res_close(R); + + dns_resconf_close(resconf); + dns_hosts_close(hosts); + dns_hints_close(hints); + dns_cache_close(cache); + + return 0; +} /* dns_res_open() */ + + +struct dns_resolver *dns_res_stub(const struct dns_options *opts, int *error) { + struct dns_resolv_conf *resconf = 0; + struct dns_hosts *hosts = 0; + struct dns_hints *hints = 0; + struct dns_resolver *res = 0; + + if (!(resconf = dns_resconf_local(error))) + goto epilog; + + if (!(hosts = dns_hosts_local(error))) + goto epilog; + + if (!(hints = dns_hints_local(resconf, error))) + goto epilog; + + if (!(res = dns_res_open(resconf, hosts, hints, NULL, opts, error))) + goto epilog; + +epilog: + dns_resconf_close(resconf); + dns_hosts_close(hosts); + dns_hints_close(hints); + + return res; +} /* dns_res_stub() */ + + +static void dns_res_frame_destroy(struct dns_resolver *R, struct dns_res_frame *frame) { + (void)R; + + dns_p_setptr(&frame->query, NULL); + dns_p_setptr(&frame->answer, NULL); + dns_p_setptr(&frame->hints, NULL); +} /* dns_res_frame_destroy() */ + + +static void dns_res_frame_init(struct dns_resolver *R, struct dns_res_frame *frame) { + memset(frame, '\0', sizeof *frame); + + /* + * NB: Can be invoked from dns_res_open, before R->resconf has been + * initialized. + */ + if (R->resconf) { + if (!R->resconf->options.recurse) + frame->qflags |= DNS_Q_RD; + if (R->resconf->options.edns0) + frame->qflags |= DNS_Q_EDNS0; + } +} /* dns_res_frame_init() */ + + +static void dns_res_frame_reset(struct dns_resolver *R, struct dns_res_frame *frame) { + dns_res_frame_destroy(R, frame); + dns_res_frame_init(R, frame); +} /* dns_res_frame_reset() */ + + +static dns_error_t dns_res_frame_prepare(struct dns_resolver *R, struct dns_res_frame *F, const char *qname, enum dns_type qtype, enum dns_class qclass) { + struct dns_packet *P = NULL; + + if (!(F < endof(R->stack))) + return DNS_EUNKNOWN; + + dns_p_movptr(&P, &F->query); + dns_res_frame_reset(R, F); + dns_p_movptr(&F->query, &P); + + return dns_q_make(&F->query, qname, qtype, qclass, F->qflags); +} /* dns_res_frame_prepare() */ + + +void dns_res_reset(struct dns_resolver *R) { + unsigned i; + + dns_so_reset(&R->so); + dns_p_setptr(&R->nodata, NULL); + + for (i = 0; i < lengthof(R->stack); i++) + dns_res_frame_destroy(R, &R->stack[i]); + + memset(&R->qname, '\0', sizeof *R - offsetof(struct dns_resolver, qname)); + + for (i = 0; i < lengthof(R->stack); i++) + dns_res_frame_init(R, &R->stack[i]); +} /* dns_res_reset() */ + + +void dns_res_close(struct dns_resolver *R) { + if (!R || 1 < dns_res_release(R)) + return; + + dns_res_reset(R); + + dns_so_destroy(&R->so); + + dns_hints_close(R->hints); + dns_hosts_close(R->hosts); + dns_resconf_close(R->resconf); + dns_cache_close(R->cache); + + free(R); +} /* dns_res_close() */ + + +dns_refcount_t dns_res_acquire(struct dns_resolver *R) { + return dns_atomic_fetch_add(&R->refcount); +} /* dns_res_acquire() */ + + +dns_refcount_t dns_res_release(struct dns_resolver *R) { + return dns_atomic_fetch_sub(&R->refcount); +} /* dns_res_release() */ + + +struct dns_resolver *dns_res_mortal(struct dns_resolver *res) { + if (res) + dns_res_release(res); + return res; +} /* dns_res_mortal() */ + + +static struct dns_packet *dns_res_merge(struct dns_packet *P0, struct dns_packet *P1, int *error_) { + size_t bufsiz = P0->end + P1->end; + struct dns_packet *P[3] = { P0, P1, 0 }; + struct dns_rr rr[3]; + int error, copy, i; + enum dns_section section; + +retry: + if (!(P[2] = dns_p_make(bufsiz, &error))) + goto error; + + dns_rr_foreach(&rr[0], P[0], .section = DNS_S_QD) { + if ((error = dns_rr_copy(P[2], &rr[0], P[0]))) + goto error; + } + + for (section = DNS_S_AN; (DNS_S_ALL & section); section <<= 1) { + for (i = 0; i < 2; i++) { + dns_rr_foreach(&rr[i], P[i], .section = section) { + copy = 1; + + dns_rr_foreach(&rr[2], P[2], .type = rr[i].type, .section = (DNS_S_ALL & ~DNS_S_QD)) { + if (0 == dns_rr_cmp(&rr[i], P[i], &rr[2], P[2])) { + copy = 0; + + break; + } + } + + if (copy && (error = dns_rr_copy(P[2], &rr[i], P[i]))) { + if (error == DNS_ENOBUFS && bufsiz < 65535) { + dns_p_setptr(&P[2], NULL); + + bufsiz = DNS_PP_MAX(65535, bufsiz * 2); + + goto retry; + } + + goto error; + } + } /* foreach(rr) */ + } /* foreach(packet) */ + } /* foreach(section) */ + + return P[2]; +error: + *error_ = error; + + dns_p_free(P[2]); + + return 0; +} /* dns_res_merge() */ + + +static struct dns_packet *dns_res_glue(struct dns_resolver *R, struct dns_packet *Q) { + struct dns_packet *P = dns_p_new(512); + char qname[DNS_D_MAXNAME + 1]; + size_t qlen; + enum dns_type qtype; + struct dns_rr rr; + unsigned sp; + int error; + + if (!(qlen = dns_d_expand(qname, sizeof qname, 12, Q, &error)) + || qlen >= sizeof qname) + return 0; + + if (!(qtype = dns_rr_type(12, Q))) + return 0; + + if ((error = dns_p_push(P, DNS_S_QD, qname, strlen(qname), qtype, DNS_C_IN, 0, 0))) + return 0; + + for (sp = 0; sp <= R->sp; sp++) { + if (!R->stack[sp].answer) + continue; + + dns_rr_foreach(&rr, R->stack[sp].answer, .name = qname, .type = qtype, .section = (DNS_S_ALL & ~DNS_S_QD)) { + rr.section = DNS_S_AN; + + if ((error = dns_rr_copy(P, &rr, R->stack[sp].answer))) + return 0; + } + } + + if (dns_p_count(P, DNS_S_AN) > 0) + goto copy; + + /* Otherwise, look for a CNAME */ + for (sp = 0; sp <= R->sp; sp++) { + if (!R->stack[sp].answer) + continue; + + dns_rr_foreach(&rr, R->stack[sp].answer, .name = qname, .type = DNS_T_CNAME, .section = (DNS_S_ALL & ~DNS_S_QD)) { + rr.section = DNS_S_AN; + + if ((error = dns_rr_copy(P, &rr, R->stack[sp].answer))) + return 0; + } + } + + if (!dns_p_count(P, DNS_S_AN)) + return 0; + +copy: + return dns_p_copy(dns_p_make(P->end, &error), P); +} /* dns_res_glue() */ + + +/* + * Sort NS records by three criteria: + * + * 1) Whether glue is present. + * 2) Whether glue record is original or of recursive lookup. + * 3) Randomly shuffle records which share the above criteria. + * + * NOTE: Assumes only NS records passed, AND ASSUMES no new NS records will + * be added during an iteration. + * + * FIXME: Only groks A glue, not AAAA glue. + */ +static int dns_res_nameserv_cmp(struct dns_rr *a, struct dns_rr *b, struct dns_rr_i *i, struct dns_packet *P) { + _Bool glued[2] = { 0 }; + struct dns_rr x = { 0 }, y = { 0 }; + struct dns_ns ns; + int cmp, error; + + if (!(error = dns_ns_parse(&ns, a, P))) + glued[0] = !!dns_rr_grep(&x, 1, dns_rr_i_new(P, .section = (DNS_S_ALL & ~DNS_S_QD), .name = ns.host, .type = DNS_T_A), P, &error); + + if (!(error = dns_ns_parse(&ns, b, P))) + glued[1] = !!dns_rr_grep(&y, 1, dns_rr_i_new(P, .section = (DNS_S_ALL & ~DNS_S_QD), .name = ns.host, .type = DNS_T_A), P, &error); + + if ((cmp = glued[1] - glued[0])) { + return cmp; + } else if ((cmp = (dns_rr_offset(&y) < i->args[0]) - (dns_rr_offset(&x) < i->args[0]))) { + return cmp; + } else { + return dns_rr_i_shuffle(a, b, i, P); + } +} /* dns_res_nameserv_cmp() */ + + +#define dgoto(sp, i) \ + do { R->stack[(sp)].state = (i); goto exec; } while (0) + +static int dns_res_exec(struct dns_resolver *R) { + struct dns_res_frame *F; + struct dns_packet *P; + union { + char host[DNS_D_MAXNAME + 1]; + char name[DNS_D_MAXNAME + 1]; + struct dns_ns ns; + struct dns_cname cname; + } u; + size_t len; + struct dns_rr rr; + int error; + +exec: + + F = &R->stack[R->sp]; + + switch (F->state) { + case DNS_R_INIT: + F->state++; + case DNS_R_GLUE: + if (R->sp == 0) + dgoto(R->sp, DNS_R_SWITCH); + + if (!F->query) + goto noquery; + + if (!(F->answer = dns_res_glue(R, F->query))) + dgoto(R->sp, DNS_R_SWITCH); + + if (!(len = dns_d_expand(u.name, sizeof u.name, 12, F->query, &error))) + goto error; + else if (len >= sizeof u.name) + goto toolong; + + dns_rr_foreach(&rr, F->answer, .name = u.name, .type = dns_rr_type(12, F->query), .section = DNS_S_AN) { + dgoto(R->sp, DNS_R_FINISH); + } + + dns_rr_foreach(&rr, F->answer, .name = u.name, .type = DNS_T_CNAME, .section = DNS_S_AN) { + F->ans_cname = rr; + + dgoto(R->sp, DNS_R_CNAME0_A); + } + + F->state++; + case DNS_R_SWITCH: + while (F->which < (int)sizeof R->resconf->lookup && R->resconf->lookup[F->which]) { + switch (R->resconf->lookup[F->which++]) { + case 'b': case 'B': + dgoto(R->sp, DNS_R_BIND); + case 'f': case 'F': + dgoto(R->sp, DNS_R_FILE); + case 'c': case 'C': + if (R->cache) + dgoto(R->sp, DNS_R_CACHE); + + break; + default: + break; + } + } + + /* + * FIXME: Examine more closely whether our logic is correct + * and DNS_R_SERVFAIL is the correct default response. + * + * Case 1: We got here because we never got an answer on the + * wire. All queries timed-out and we reached maximum + * attempts count. See DNS_R_FOREACH_NS. In that case + * DNS_R_SERVFAIL is the correct state, unless we want to + * return DNS_ETIMEDOUT. + * + * Case 2: We were a stub resolver and got an unsatisfactory + * answer (empty ANSWER section) which caused us to jump + * back to DNS_R_SEARCH and ultimately to DNS_R_SWITCH. We + * return the answer returned from the wire, which we + * stashed in R->nodata. + * + * Case 3: We reached maximum attempts count as in case #1, + * but never got an authoritative response which caused us + * to short-circuit. See end of DNS_R_QUERY_A case. We + * should probably prepare R->nodata as in case #2. + */ + if (R->sp == 0 && R->nodata) { /* XXX: can we just return nodata regardless? */ + dns_p_movptr(&F->answer, &R->nodata); + dgoto(R->sp, DNS_R_FINISH); + } + + dgoto(R->sp, DNS_R_SERVFAIL); + case DNS_R_FILE: + if (R->sp > 0) { + if (!dns_p_setptr(&F->answer, dns_hosts_query(R->hosts, F->query, &error))) + goto error; + + if (dns_p_count(F->answer, DNS_S_AN) > 0) + dgoto(R->sp, DNS_R_FINISH); + + dns_p_setptr(&F->answer, NULL); + } else { + R->search = 0; + + while ((len = dns_resconf_search(u.name, sizeof u.name, R->qname, R->qlen, R->resconf, &R->search))) { + if ((error = dns_q_make2(&F->query, u.name, len, R->qtype, R->qclass, F->qflags))) + goto error; + + if (!dns_p_setptr(&F->answer, dns_hosts_query(R->hosts, F->query, &error))) + goto error; + + if (dns_p_count(F->answer, DNS_S_AN) > 0) + dgoto(R->sp, DNS_R_FINISH); + + dns_p_setptr(&F->answer, NULL); + } + } + + dgoto(R->sp, DNS_R_SWITCH); + case DNS_R_CACHE: + error = 0; + + if (!F->query && (error = dns_q_make(&F->query, R->qname, R->qtype, R->qclass, F->qflags))) + goto error; + + if (dns_p_setptr(&F->answer, R->cache->query(F->query, R->cache, &error))) { + if (dns_p_count(F->answer, DNS_S_AN) > 0) + dgoto(R->sp, DNS_R_FINISH); + + dns_p_setptr(&F->answer, NULL); + + dgoto(R->sp, DNS_R_SWITCH); + } else if (error) + goto error; + + F->state++; + case DNS_R_SUBMIT: + if ((error = R->cache->submit(F->query, R->cache))) + goto error; + + F->state++; + case DNS_R_CHECK: + if ((error = R->cache->check(R->cache))) + goto error; + + F->state++; + case DNS_R_FETCH: + error = 0; + + if (dns_p_setptr(&F->answer, R->cache->fetch(R->cache, &error))) { + if (dns_p_count(F->answer, DNS_S_AN) > 0) + dgoto(R->sp, DNS_R_FINISH); + + dns_p_setptr(&F->answer, NULL); + + dgoto(R->sp, DNS_R_SWITCH); + } else if (error) + goto error; + + dgoto(R->sp, DNS_R_SWITCH); + case DNS_R_BIND: + if (R->sp > 0) { + if (!F->query) + goto noquery; + + dgoto(R->sp, DNS_R_HINTS); + } + + R->search = 0; + + F->state++; + case DNS_R_SEARCH: + /* + * XXX: We probably should only apply the domain search + * algorithm if R->sp == 0. + */ + if (!(len = dns_resconf_search(u.name, sizeof u.name, R->qname, R->qlen, R->resconf, &R->search))) + dgoto(R->sp, DNS_R_SWITCH); + + if ((error = dns_q_make2(&F->query, u.name, len, R->qtype, R->qclass, F->qflags))) + goto error; + + F->state++; + case DNS_R_HINTS: + if (!dns_p_setptr(&F->hints, dns_hints_query(R->hints, F->query, &error))) + goto error; + + F->state++; + case DNS_R_ITERATE: + dns_rr_i_init(&F->hints_i, F->hints); + + F->hints_i.section = DNS_S_AUTHORITY; + F->hints_i.type = DNS_T_NS; + F->hints_i.sort = &dns_res_nameserv_cmp; + F->hints_i.args[0] = F->hints->end; + + F->state++; + case DNS_R_FOREACH_NS: + dns_rr_i_save(&F->hints_i); + + /* Load our next nameserver host. */ + if (!dns_rr_grep(&F->hints_ns, 1, &F->hints_i, F->hints, &error)) { + if (++F->attempts < R->resconf->options.attempts) + dgoto(R->sp, DNS_R_ITERATE); + + dgoto(R->sp, DNS_R_SWITCH); + } + + dns_rr_i_init(&F->hints_j, F->hints); + + /* Assume there are glue records */ + dgoto(R->sp, DNS_R_FOREACH_A); + case DNS_R_RESOLV0_NS: + /* Have we reached our max depth? */ + if (&F[1] >= endof(R->stack)) + dgoto(R->sp, DNS_R_FOREACH_NS); + + if ((error = dns_ns_parse(&u.ns, &F->hints_ns, F->hints))) + goto error; + if ((error = dns_res_frame_prepare(R, &F[1], u.ns.host, DNS_T_A, DNS_C_IN))) + goto error; + + F->state++; + + dgoto(++R->sp, DNS_R_INIT); + case DNS_R_RESOLV1_NS: + if (!(len = dns_d_expand(u.host, sizeof u.host, 12, F[1].query, &error))) + goto error; + else if (len >= sizeof u.host) + goto toolong; + + dns_rr_foreach(&rr, F[1].answer, .name = u.host, .type = DNS_T_A, .section = (DNS_S_ALL & ~DNS_S_QD)) { + rr.section = DNS_S_AR; + + if ((error = dns_rr_copy(F->hints, &rr, F[1].answer))) + goto error; + + dns_rr_i_rewind(&F->hints_i); /* Now there's glue. */ + } + + dgoto(R->sp, DNS_R_FOREACH_NS); + case DNS_R_FOREACH_A: { + struct dns_a a; + struct sockaddr_in sin; + + /* + * NOTE: Iterator initialized in DNS_R_FOREACH_NS because + * this state is re-entrant, but we need to reset + * .name to a valid pointer each time. + */ + if ((error = dns_ns_parse(&u.ns, &F->hints_ns, F->hints))) + goto error; + + F->hints_j.name = u.ns.host; + F->hints_j.type = DNS_T_A; + F->hints_j.section = DNS_S_ALL & ~DNS_S_QD; + + if (!dns_rr_grep(&rr, 1, &F->hints_j, F->hints, &error)) { + if (!dns_rr_i_count(&F->hints_j)) + dgoto(R->sp, DNS_R_RESOLV0_NS); + + dgoto(R->sp, DNS_R_FOREACH_NS); + } + + if ((error = dns_a_parse(&a, &rr, F->hints))) + goto error; + + sin.sin_family = AF_INET; + sin.sin_addr = a.addr; + if (R->sp == 0) + sin.sin_port = dns_hints_port(R->hints, AF_INET, &sin.sin_addr); + else + sin.sin_port = htons(53); + + if (DNS_DEBUG) { + char addr[INET_ADDRSTRLEN + 1]; + dns_a_print(addr, sizeof addr, &a); + dns_header(F->query)->qid = dns_so_mkqid(&R->so); + DNS_SHOW(F->query, "ASKING: %s/%s @ DEPTH: %u)", u.ns.host, addr, R->sp); + } + + if ((error = dns_so_submit(&R->so, F->query, (struct sockaddr *)&sin))) + goto error; + + F->state++; + } + case DNS_R_QUERY_A: + if (dns_so_elapsed(&R->so) >= dns_resconf_timeout(R->resconf)) + dgoto(R->sp, DNS_R_FOREACH_A); + + if ((error = dns_so_check(&R->so))) + goto error; + + if (!dns_p_setptr(&F->answer, dns_so_fetch(&R->so, &error))) + goto error; + + if (DNS_DEBUG) { + DNS_SHOW(F->answer, "ANSWER @ DEPTH: %u)", R->sp); + } + + if (dns_p_rcode(F->answer) == DNS_RC_FORMERR || + dns_p_rcode(F->answer) == DNS_RC_NOTIMP || + dns_p_rcode(F->answer) == DNS_RC_BADVERS) { + /* Temporarily disable EDNS0 and try again. */ + if (F->qflags & DNS_Q_EDNS0) { + F->qflags &= ~DNS_Q_EDNS0; + if ((error = dns_q_remake(&F->query, F->qflags))) + goto error; + + dgoto(R->sp, DNS_R_FOREACH_A); + } + } + + if ((error = dns_rr_parse(&rr, 12, F->query))) + goto error; + + if (!(len = dns_d_expand(u.name, sizeof u.name, rr.dn.p, F->query, &error))) + goto error; + else if (len >= sizeof u.name) + goto toolong; + + dns_rr_foreach(&rr, F->answer, .section = DNS_S_AN, .name = u.name, .type = rr.type) { + dgoto(R->sp, DNS_R_FINISH); /* Found */ + } + + dns_rr_foreach(&rr, F->answer, .section = DNS_S_AN, .name = u.name, .type = DNS_T_CNAME) { + F->ans_cname = rr; + + dgoto(R->sp, DNS_R_CNAME0_A); + } + + /* + * XXX: The condition here should probably check whether + * R->sp == 0, because DNS_R_SEARCH runs regardless of + * options.recurse. See DNS_R_BIND. + */ + if (!R->resconf->options.recurse) { + /* Make first answer our tentative answer */ + if (!R->nodata) + dns_p_movptr(&R->nodata, &F->answer); + + dgoto(R->sp, DNS_R_SEARCH); + } + + dns_rr_foreach(&rr, F->answer, .section = DNS_S_NS, .type = DNS_T_NS) { + dns_p_movptr(&F->hints, &F->answer); + + dgoto(R->sp, DNS_R_ITERATE); + } + + /* XXX: Should this go further up? */ + if (dns_header(F->answer)->aa) + dgoto(R->sp, DNS_R_FINISH); + + /* XXX: Should we copy F->answer to R->nodata? */ + + dgoto(R->sp, DNS_R_FOREACH_A); + case DNS_R_CNAME0_A: + if (&F[1] >= endof(R->stack)) + dgoto(R->sp, DNS_R_FINISH); + + if ((error = dns_cname_parse(&u.cname, &F->ans_cname, F->answer))) + goto error; + if ((error = dns_res_frame_prepare(R, &F[1], u.cname.host, dns_rr_type(12, F->query), DNS_C_IN))) + goto error; + + F->state++; + + dgoto(++R->sp, DNS_R_INIT); + case DNS_R_CNAME1_A: + if (!(P = dns_res_merge(F->answer, F[1].answer, &error))) + goto error; + + dns_p_setptr(&F->answer, P); + + dgoto(R->sp, DNS_R_FINISH); + case DNS_R_FINISH: + if (!F->answer) + goto noanswer; + + if (!R->resconf->options.smart || R->sp > 0) + dgoto(R->sp, DNS_R_DONE); + + R->smart.section = DNS_S_AN; + R->smart.type = R->qtype; + + dns_rr_i_init(&R->smart, F->answer); + + F->state++; + case DNS_R_SMART0_A: + if (&F[1] >= endof(R->stack)) + dgoto(R->sp, DNS_R_DONE); + + while (dns_rr_grep(&rr, 1, &R->smart, F->answer, &error)) { + union { + struct dns_ns ns; + struct dns_mx mx; + struct dns_srv srv; + } rd; + const char *qname; + enum dns_type qtype; + enum dns_class qclass; + + switch (rr.type) { + case DNS_T_NS: + if ((error = dns_ns_parse(&rd.ns, &rr, F->answer))) + goto error; + + qname = rd.ns.host; + qtype = DNS_T_A; + qclass = DNS_C_IN; + + break; + case DNS_T_MX: + if ((error = dns_mx_parse(&rd.mx, &rr, F->answer))) + goto error; + + qname = rd.mx.host; + qtype = DNS_T_A; + qclass = DNS_C_IN; + + break; + case DNS_T_SRV: + if ((error = dns_srv_parse(&rd.srv, &rr, F->answer))) + goto error; + + qname = rd.srv.target; + qtype = DNS_T_A; + qclass = DNS_C_IN; + + break; + default: + continue; + } /* switch() */ + + if ((error = dns_res_frame_prepare(R, &F[1], qname, qtype, qclass))) + goto error; + + F->state++; + + dgoto(++R->sp, DNS_R_INIT); + } /* while() */ + + /* + * NOTE: SMTP specification says to fallback to A record. + * + * XXX: Should we add a mock MX answer? + */ + if (R->qtype == DNS_T_MX && R->smart.state.count == 0) { + if ((error = dns_res_frame_prepare(R, &F[1], R->qname, DNS_T_A, DNS_C_IN))) + goto error; + + R->smart.state.count++; + F->state++; + + dgoto(++R->sp, DNS_R_INIT); + } + + dgoto(R->sp, DNS_R_DONE); + case DNS_R_SMART1_A: + if (!F[1].answer) + goto noanswer; + + /* + * FIXME: For CNAME chains (which are typically illegal in + * this context), we should rewrite the record host name + * to the original smart qname. All the user cares about + * is locating that A/AAAA record. + */ + dns_rr_foreach(&rr, F[1].answer, .section = DNS_S_AN, .type = DNS_T_A) { + rr.section = DNS_S_AR; + + if (dns_rr_exists(&rr, F[1].answer, F->answer)) + continue; + + while ((error = dns_rr_copy(F->answer, &rr, F[1].answer))) { + if (error != DNS_ENOBUFS) + goto error; + if ((error = dns_p_grow(&F->answer))) + goto error; + } + } + + dgoto(R->sp, DNS_R_SMART0_A); + case DNS_R_DONE: + if (!F->answer) + goto noanswer; + + if (R->sp > 0) + dgoto(--R->sp, F[-1].state); + + break; + case DNS_R_SERVFAIL: + if (!dns_p_setptr(&F->answer, dns_p_make(DNS_P_QBUFSIZ, &error))) + goto error; + + dns_header(F->answer)->qr = 1; + dns_header(F->answer)->rcode = DNS_RC_SERVFAIL; + + if ((error = dns_p_push(F->answer, DNS_S_QD, R->qname, strlen(R->qname), R->qtype, R->qclass, 0, 0))) + goto error; + + dgoto(R->sp, DNS_R_DONE); + default: + error = EINVAL; + + goto error; + } /* switch () */ + + return 0; +noquery: + error = DNS_ENOQUERY; + + goto error; +noanswer: + error = DNS_ENOANSWER; + + goto error; +toolong: + error = DNS_EILLEGAL; + + /* FALL THROUGH */ +error: + return error; +} /* dns_res_exec() */ + +#undef goto + + +void dns_res_clear(struct dns_resolver *R) { + switch (R->stack[R->sp].state) { + case DNS_R_CHECK: + R->cache->clear(R->cache); + break; + default: + dns_so_clear(&R->so); + break; + } +} /* dns_res_clear() */ + + +static int dns_res_events2(struct dns_resolver *R, enum dns_events type) { + int events; + + switch (R->stack[R->sp].state) { + case DNS_R_CHECK: + events = R->cache->events(R->cache); + + return (type == DNS_LIBEVENT)? DNS_POLL2EV(events) : events; + default: + return dns_so_events2(&R->so, type); + } +} /* dns_res_events2() */ + + +int dns_res_events(struct dns_resolver *R) { + return dns_res_events2(R, R->so.opts.events); +} /* dns_res_events() */ + + +int dns_res_pollfd(struct dns_resolver *R) { + switch (R->stack[R->sp].state) { + case DNS_R_CHECK: + return R->cache->pollfd(R->cache); + default: + return dns_so_pollfd(&R->so); + } +} /* dns_res_pollfd() */ + + +time_t dns_res_timeout(struct dns_resolver *R) { + time_t elapsed; + + switch (R->stack[R->sp].state) { +#if 0 + case DNS_R_QUERY_AAAA: +#endif + case DNS_R_QUERY_A: + elapsed = dns_so_elapsed(&R->so); + + if (elapsed <= dns_resconf_timeout(R->resconf)) + return R->resconf->options.timeout - elapsed; + + break; + default: + break; + } /* switch() */ + + /* + * NOTE: We're not in a pollable state, or the user code hasn't + * called dns_res_check properly. The calling code is probably + * broken. Put them into a slow-burn pattern. + */ + return 1; +} /* dns_res_timeout() */ + + +time_t dns_res_elapsed(struct dns_resolver *R) { + return dns_elapsed(&R->elapsed); +} /* dns_res_elapsed() */ + + +int dns_res_poll(struct dns_resolver *R, int timeout) { + return dns_poll(dns_res_pollfd(R), dns_res_events2(R, DNS_SYSPOLL), timeout); +} /* dns_res_poll() */ + + +int dns_res_submit2(struct dns_resolver *R, const char *qname, size_t qlen, enum dns_type qtype, enum dns_class qclass) { + dns_res_reset(R); + + /* Don't anchor; that can conflict with searchlist generation. */ + dns_d_init(R->qname, sizeof R->qname, qname, (R->qlen = qlen), 0); + + R->qtype = qtype; + R->qclass = qclass; + + dns_begin(&R->elapsed); + + return 0; +} /* dns_res_submit2() */ + + +int dns_res_submit(struct dns_resolver *R, const char *qname, enum dns_type qtype, enum dns_class qclass) { + return dns_res_submit2(R, qname, strlen(qname), qtype, qclass); +} /* dns_res_submit() */ + + +int dns_res_check(struct dns_resolver *R) { + int error; + + if (R->stack[0].state != DNS_R_DONE) { + if ((error = dns_res_exec(R))) + return error; + } + + return 0; +} /* dns_res_check() */ + + +struct dns_packet *dns_res_fetch(struct dns_resolver *R, int *error) { + struct dns_packet *P = NULL; + + if (R->stack[0].state != DNS_R_DONE) + return *error = DNS_EUNKNOWN, (void *)0; + + if (!dns_p_movptr(&P, &R->stack[0].answer)) + return *error = DNS_EFETCHED, (void *)0; + + return P; +} /* dns_res_fetch() */ + + +static struct dns_packet *dns_res_fetch_and_study(struct dns_resolver *R, int *_error) { + struct dns_packet *P = NULL; + int error; + + if (!(P = dns_res_fetch(R, &error))) + goto error; + if ((error = dns_p_study(P))) + goto error; + + return P; +error: + *_error = error; + + dns_p_free(P); + + return NULL; +} /* dns_res_fetch_and_study() */ + + +struct dns_packet *dns_res_query(struct dns_resolver *res, const char *qname, enum dns_type qtype, enum dns_class qclass, int timeout, int *error_) { + int error; + + if ((error = dns_res_submit(res, qname, qtype, qclass))) + goto error; + + while ((error = dns_res_check(res))) { + if (dns_res_elapsed(res) > timeout) + error = DNS_ETIMEDOUT; + + if (error != DNS_EAGAIN) + goto error; + + if ((error = dns_res_poll(res, 1))) + goto error; + } + + return dns_res_fetch(res, error_); +error: + *error_ = error; + + return 0; +} /* dns_res_query() */ + + +const struct dns_stat *dns_res_stat(struct dns_resolver *res) { + return dns_so_stat(&res->so); +} /* dns_res_stat() */ + + +void dns_res_sethints(struct dns_resolver *res, struct dns_hints *hints) { + dns_hints_acquire(hints); /* acquire first in case same hints object */ + dns_hints_close(res->hints); + res->hints = hints; +} /* dns_res_sethints() */ + + +/* + * A D D R I N F O R O U T I N E S + * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +struct dns_addrinfo { + struct addrinfo hints; + struct dns_resolver *res; + + char qname[DNS_D_MAXNAME + 1]; + enum dns_type qtype; + unsigned short qport, port; + + struct { + unsigned long todo; + int state; + int atype; + enum dns_type qtype; + } af; + + struct dns_packet *answer; + struct dns_packet *glue; + + struct dns_rr_i i, g; + struct dns_rr rr; + + char cname[DNS_D_MAXNAME + 1]; + char i_cname[DNS_D_MAXNAME + 1], g_cname[DNS_D_MAXNAME + 1]; + + int g_depth; + + int state; + int found; + + struct dns_stat st; +}; /* struct dns_addrinfo */ + + +#define DNS_AI_AFMAX 32 +#define DNS_AI_AF2INDEX(af) (1UL << ((af) - 1)) + +static inline unsigned long dns_ai_af2index(int af) { + dns_static_assert(dns_same_type(unsigned long, DNS_AI_AF2INDEX(1), 1), "internal type mismatch"); + dns_static_assert(dns_same_type(unsigned long, ((struct dns_addrinfo *)0)->af.todo, 1), "internal type mismatch"); + + return (af > 0 && af <= DNS_AI_AFMAX)? DNS_AI_AF2INDEX(af) : 0; +} + +static int dns_ai_setaf(struct dns_addrinfo *ai, int af, int qtype) { + ai->af.atype = af; + ai->af.qtype = qtype; + + ai->af.todo &= ~dns_ai_af2index(af); + + return af; +} /* dns_ai_setaf() */ + +#define DNS_SM_RESTORE \ + do { pc = 0xff & (ai->af.state >> 0); i = 0xff & (ai->af.state >> 8); } while (0) +#define DNS_SM_SAVE \ + do { ai->af.state = ((0xff & pc) << 0) | ((0xff & i) << 8); } while (0) + +static int dns_ai_nextaf(struct dns_addrinfo *ai) { + int i, pc; + + dns_static_assert(AF_UNSPEC == 0, "AF_UNSPEC constant not 0"); + dns_static_assert(AF_INET <= DNS_AI_AFMAX, "AF_INET constant too large"); + dns_static_assert(AF_INET6 <= DNS_AI_AFMAX, "AF_INET6 constant too large"); + + DNS_SM_ENTER; + + if (ai->res) { + /* + * NB: On OpenBSD, at least, the types of entries resolved + * is the intersection of the /etc/resolv.conf families and + * the families permitted by the .ai_type hint. So if + * /etc/resolv.conf has "family inet4" and .ai_type + * is AF_INET6, then the address ::1 will return 0 entries + * even if AI_NUMERICHOST is specified in .ai_flags. + */ + while (i < (int)lengthof(ai->res->resconf->family)) { + int af = ai->res->resconf->family[i++]; + + if (af == AF_UNSPEC) { + DNS_SM_EXIT; + } else if (af < 0 || af > DNS_AI_AFMAX) { + continue; + } else if (!(DNS_AI_AF2INDEX(af) & ai->af.todo)) { + continue; + } else if (af == AF_INET) { + DNS_SM_YIELD(dns_ai_setaf(ai, AF_INET, DNS_T_A)); + } else if (af == AF_INET6) { + DNS_SM_YIELD(dns_ai_setaf(ai, AF_INET6, DNS_T_AAAA)); + } + } + } else { + /* + * NB: If we get here than AI_NUMERICFLAGS should be set and + * order shouldn't matter. + */ + if (DNS_AI_AF2INDEX(AF_INET) & ai->af.todo) + DNS_SM_YIELD(dns_ai_setaf(ai, AF_INET, DNS_T_A)); + if (DNS_AI_AF2INDEX(AF_INET6) & ai->af.todo) + DNS_SM_YIELD(dns_ai_setaf(ai, AF_INET6, DNS_T_AAAA)); + } + + DNS_SM_LEAVE; + + return dns_ai_setaf(ai, AF_UNSPEC, 0); +} /* dns_ai_nextaf() */ + +#undef DNS_SM_RESTORE +#undef DNS_SM_SAVE + +static enum dns_type dns_ai_qtype(struct dns_addrinfo *ai) { + return (ai->qtype)? ai->qtype : ai->af.qtype; +} /* dns_ai_qtype() */ + + +static dns_error_t dns_ai_parseport(unsigned short *port, const char *serv, const struct addrinfo *hints) { + const char *cp = serv; + unsigned long n = 0; + + while (*cp >= '0' && *cp <= '9' && n < 65536) { + n *= 10; + n += *cp++ - '0'; + } + + if (*cp == '\0') { + if (cp == serv || n >= 65536) + return DNS_ESERVICE; + + *port = n; + + return 0; + } + + if (hints->ai_flags & AI_NUMERICSERV) + return DNS_ESERVICE; + + /* TODO: try getaddrinfo(NULL, serv, { .ai_flags = AI_NUMERICSERV }) */ + + return DNS_ESERVICE; +} /* dns_ai_parseport() */ + + +struct dns_addrinfo *dns_ai_open(const char *host, const char *serv, enum dns_type qtype, const struct addrinfo *hints, struct dns_resolver *res, int *_error) { + static const struct dns_addrinfo ai_initializer; + struct dns_addrinfo *ai; + int error; + + if (res) { + dns_res_acquire(res); + } else if (!(hints->ai_flags & AI_NUMERICHOST)) { + /* + * NOTE: it's assumed that *_error is set from a previous + * API function call, such as dns_res_stub(). Should change + * this semantic, but it's applied elsewhere, too. + */ + if (!*_error) + *_error = EINVAL; + return NULL; + } + + if (!(ai = malloc(sizeof *ai))) + goto syerr; + + *ai = ai_initializer; + ai->hints = *hints; + + ai->res = res; + res = NULL; + + if (sizeof ai->qname <= dns_strlcpy(ai->qname, host, sizeof ai->qname)) + { error = ENAMETOOLONG; goto error; } + + ai->qtype = qtype; + ai->qport = 0; + + if (serv && (error = dns_ai_parseport(&ai->qport, serv, hints))) + goto error; + ai->port = ai->qport; + + /* + * FIXME: If an explicit A or AAAA record type conflicts with + * .ai_family or with resconf.family (i.e. AAAA specified but + * AF_INET6 not in interection of .ai_family and resconf.family), + * then what? + */ + switch (ai->qtype) { + case DNS_T_A: + ai->af.todo = DNS_AI_AF2INDEX(AF_INET); + break; + case DNS_T_AAAA: + ai->af.todo = DNS_AI_AF2INDEX(AF_INET6); + break; + default: /* 0, MX, SRV, etc */ + switch (ai->hints.ai_family) { + case AF_UNSPEC: + ai->af.todo = DNS_AI_AF2INDEX(AF_INET) | DNS_AI_AF2INDEX(AF_INET6); + break; + case AF_INET: + ai->af.todo = DNS_AI_AF2INDEX(AF_INET); + break; + case AF_INET6: + ai->af.todo = DNS_AI_AF2INDEX(AF_INET6); + break; + default: + break; + } + } + + return ai; +syerr: + error = dns_syerr(); +error: + *_error = error; + + dns_ai_close(ai); + dns_res_close(res); + + return NULL; +} /* dns_ai_open() */ + + +void dns_ai_close(struct dns_addrinfo *ai) { + if (!ai) + return; + + dns_res_close(ai->res); + + if (ai->answer != ai->glue) + dns_p_free(ai->glue); + + dns_p_free(ai->answer); + free(ai); +} /* dns_ai_close() */ + + +static int dns_ai_setent(struct addrinfo **ent, union dns_any *any, enum dns_type type, struct dns_addrinfo *ai) { + struct sockaddr *saddr; + struct sockaddr_in sin; + struct sockaddr_in6 sin6; + const char *cname; + size_t clen; + + switch (type) { + case DNS_T_A: + saddr = memset(&sin, '\0', sizeof sin); + + sin.sin_family = AF_INET; + sin.sin_port = htons(ai->port); + + memcpy(&sin.sin_addr, any, sizeof sin.sin_addr); + + break; + case DNS_T_AAAA: + saddr = memset(&sin6, '\0', sizeof sin6); + + sin6.sin6_family = AF_INET6; + sin6.sin6_port = htons(ai->port); + + memcpy(&sin6.sin6_addr, any, sizeof sin6.sin6_addr); + + break; + default: + return EINVAL; + } /* switch() */ + + if (ai->hints.ai_flags & AI_CANONNAME) { + cname = (*ai->cname)? ai->cname : ai->qname; + clen = strlen(cname); + } else { + cname = NULL; + clen = 0; + } + + if (!(*ent = malloc(sizeof **ent + dns_sa_len(saddr) + ((ai->hints.ai_flags & AI_CANONNAME)? clen + 1 : 0)))) + return dns_syerr(); + + memset(*ent, '\0', sizeof **ent); + + (*ent)->ai_family = saddr->sa_family; + (*ent)->ai_socktype = ai->hints.ai_socktype; + (*ent)->ai_protocol = ai->hints.ai_protocol; + + (*ent)->ai_addr = memcpy((unsigned char *)*ent + sizeof **ent, saddr, dns_sa_len(saddr)); + (*ent)->ai_addrlen = dns_sa_len(saddr); + + if (ai->hints.ai_flags & AI_CANONNAME) + (*ent)->ai_canonname = memcpy((unsigned char *)*ent + sizeof **ent + dns_sa_len(saddr), cname, clen + 1); + + ai->found++; + + return 0; +} /* dns_ai_setent() */ + + +enum dns_ai_state { + DNS_AI_S_INIT, + DNS_AI_S_NEXTAF, + DNS_AI_S_NUMERIC, + DNS_AI_S_SUBMIT, + DNS_AI_S_CHECK, + DNS_AI_S_FETCH, + DNS_AI_S_FOREACH_I, + DNS_AI_S_INIT_G, + DNS_AI_S_ITERATE_G, + DNS_AI_S_FOREACH_G, + DNS_AI_S_SUBMIT_G, + DNS_AI_S_CHECK_G, + DNS_AI_S_FETCH_G, + DNS_AI_S_DONE, +}; /* enum dns_ai_state */ + +#define dns_ai_goto(which) do { ai->state = (which); goto exec; } while (0) + +int dns_ai_nextent(struct addrinfo **ent, struct dns_addrinfo *ai) { + struct dns_packet *ans, *glue; + struct dns_rr rr; + char qname[DNS_D_MAXNAME + 1]; + union dns_any any; + size_t qlen, clen; + int error; + + *ent = 0; + +exec: + + switch (ai->state) { + case DNS_AI_S_INIT: + ai->state++; + case DNS_AI_S_NEXTAF: + if (!dns_ai_nextaf(ai)) + dns_ai_goto(DNS_AI_S_DONE); + + ai->state++; + case DNS_AI_S_NUMERIC: + if (1 == dns_inet_pton(AF_INET, ai->qname, &any.a)) { + if (ai->af.atype == AF_INET) { + ai->state = DNS_AI_S_NEXTAF; + return dns_ai_setent(ent, &any, DNS_T_A, ai); + } else { + dns_ai_goto(DNS_AI_S_NEXTAF); + } + } + + if (1 == dns_inet_pton(AF_INET6, ai->qname, &any.aaaa)) { + if (ai->af.atype == AF_INET6) { + ai->state = DNS_AI_S_NEXTAF; + return dns_ai_setent(ent, &any, DNS_T_AAAA, ai); + } else { + dns_ai_goto(DNS_AI_S_NEXTAF); + } + } + + if (ai->hints.ai_flags & AI_NUMERICHOST) + dns_ai_goto(DNS_AI_S_NEXTAF); + + ai->state++; + case DNS_AI_S_SUBMIT: + assert(ai->res); + + if ((error = dns_res_submit(ai->res, ai->qname, dns_ai_qtype(ai), DNS_C_IN))) + return error; + + ai->state++; + case DNS_AI_S_CHECK: + if ((error = dns_res_check(ai->res))) + return error; + + ai->state++; + case DNS_AI_S_FETCH: + if (!(ans = dns_res_fetch_and_study(ai->res, &error))) + return error; + if (ai->glue != ai->answer) + dns_p_free(ai->glue); + ai->glue = dns_p_movptr(&ai->answer, &ans); + + /* Search generator may have changed the qname. */ + if (!(qlen = dns_d_expand(qname, sizeof qname, 12, ai->answer, &error))) + return error; + else if (qlen >= sizeof qname) + return DNS_EILLEGAL; + if (!dns_d_cname(ai->cname, sizeof ai->cname, qname, qlen, ai->answer, &error)) + return error; + + dns_strlcpy(ai->i_cname, ai->cname, sizeof ai->i_cname); + dns_rr_i_init(&ai->i, ai->answer); + ai->i.section = DNS_S_AN; + ai->i.name = ai->i_cname; + ai->i.type = dns_ai_qtype(ai); + ai->i.sort = &dns_rr_i_order; + + ai->state++; + case DNS_AI_S_FOREACH_I: + if (!dns_rr_grep(&rr, 1, &ai->i, ai->answer, &error)) + dns_ai_goto(DNS_AI_S_NEXTAF); + + if ((error = dns_any_parse(&any, &rr, ai->answer))) + return error; + + ai->port = ai->qport; + + switch (rr.type) { + case DNS_T_A: + case DNS_T_AAAA: + return dns_ai_setent(ent, &any, rr.type, ai); + default: + if (!(clen = dns_any_cname(ai->cname, sizeof ai->cname, &any, rr.type))) + dns_ai_goto(DNS_AI_S_FOREACH_I); + + /* + * Find the "real" canonical name. Some authorities + * publish aliases where an RFC defines a canonical + * name. We trust that the resolver followed any + * CNAME chains on it's own, regardless of whether + * the "smart" option is enabled. + */ + if (!dns_d_cname(ai->cname, sizeof ai->cname, ai->cname, clen, ai->answer, &error)) + return error; + + if (rr.type == DNS_T_SRV) + ai->port = any.srv.port; + + break; + } /* switch() */ + + ai->state++; + case DNS_AI_S_INIT_G: + ai->g_depth = 0; + + ai->state++; + case DNS_AI_S_ITERATE_G: + dns_strlcpy(ai->g_cname, ai->cname, sizeof ai->g_cname); + dns_rr_i_init(&ai->g, ai->glue); + ai->g.section = DNS_S_ALL & ~DNS_S_QD; + ai->g.name = ai->g_cname; + ai->g.type = ai->af.qtype; + + ai->state++; + case DNS_AI_S_FOREACH_G: + if (!dns_rr_grep(&rr, 1, &ai->g, ai->glue, &error)) { + if (dns_rr_i_count(&ai->g) > 0) + dns_ai_goto(DNS_AI_S_FOREACH_I); + else + dns_ai_goto(DNS_AI_S_SUBMIT_G); + } + + if ((error = dns_any_parse(&any, &rr, ai->glue))) + return error; + + return dns_ai_setent(ent, &any, rr.type, ai); + case DNS_AI_S_SUBMIT_G: + /* skip if already queried */ + if (dns_rr_grep(&rr, 1, dns_rr_i_new(ai->glue, .section = DNS_S_QD, .name = ai->g.name, .type = ai->g.type), ai->glue, &error)) + dns_ai_goto(DNS_AI_S_FOREACH_I); + /* skip if we recursed (CNAME chains should have been handled in the resolver) */ + if (++ai->g_depth > 1) + dns_ai_goto(DNS_AI_S_FOREACH_I); + + if ((error = dns_res_submit(ai->res, ai->g.name, ai->g.type, DNS_C_IN))) + return error; + + ai->state++; + case DNS_AI_S_CHECK_G: + if ((error = dns_res_check(ai->res))) + return error; + + ai->state++; + case DNS_AI_S_FETCH_G: + if (!(ans = dns_res_fetch_and_study(ai->res, &error))) + return error; + + glue = dns_p_merge(ai->glue, DNS_S_ALL, ans, DNS_S_ALL, &error); + dns_p_setptr(&ans, NULL); + if (!glue) + return error; + + if (ai->glue != ai->answer) + dns_p_free(ai->glue); + ai->glue = glue; + + if (!dns_d_cname(ai->cname, sizeof ai->cname, ai->g.name, strlen(ai->g.name), ai->glue, &error)) + dns_ai_goto(DNS_AI_S_FOREACH_I); + + dns_ai_goto(DNS_AI_S_ITERATE_G); + case DNS_AI_S_DONE: + if (ai->found) { + return ENOENT; /* TODO: Just return 0 */ + } else if (ai->answer) { + switch (dns_p_rcode(ai->answer)) { + case DNS_RC_NOERROR: + /* FALL THROUGH */ + case DNS_RC_NXDOMAIN: + return DNS_ENONAME; + default: + return DNS_EFAIL; + } + } else { + return DNS_EFAIL; + } + default: + return EINVAL; + } /* switch() */ +} /* dns_ai_nextent() */ + + +time_t dns_ai_elapsed(struct dns_addrinfo *ai) { + return (ai->res)? dns_res_elapsed(ai->res) : 0; +} /* dns_ai_elapsed() */ + + +void dns_ai_clear(struct dns_addrinfo *ai) { + if (ai->res) + dns_res_clear(ai->res); +} /* dns_ai_clear() */ + + +int dns_ai_events(struct dns_addrinfo *ai) { + return (ai->res)? dns_res_events(ai->res) : 0; +} /* dns_ai_events() */ + + +int dns_ai_pollfd(struct dns_addrinfo *ai) { + return (ai->res)? dns_res_pollfd(ai->res) : -1; +} /* dns_ai_pollfd() */ + + +time_t dns_ai_timeout(struct dns_addrinfo *ai) { + return (ai->res)? dns_res_timeout(ai->res) : 0; +} /* dns_ai_timeout() */ + + +int dns_ai_poll(struct dns_addrinfo *ai, int timeout) { + return (ai->res)? dns_res_poll(ai->res, timeout) : 0; +} /* dns_ai_poll() */ + + +size_t dns_ai_print(void *_dst, size_t lim, struct addrinfo *ent, struct dns_addrinfo *ai) { + struct dns_buf dst = DNS_B_INTO(_dst, lim); + char addr[DNS_PP_MAX(INET_ADDRSTRLEN, INET6_ADDRSTRLEN) + 1]; + + dns_b_puts(&dst, "[ "); + dns_b_puts(&dst, ai->qname); + dns_b_puts(&dst, " IN "); + if (ai->qtype) { + dns_b_puts(&dst, dns_strtype(ai->qtype)); + } else if (ent->ai_family == AF_INET) { + dns_b_puts(&dst, dns_strtype(DNS_T_A)); + } else if (ent->ai_family == AF_INET6) { + dns_b_puts(&dst, dns_strtype(DNS_T_AAAA)); + } else { + dns_b_puts(&dst, "0"); + } + dns_b_puts(&dst, " ]\n"); + + dns_b_puts(&dst, ".ai_family = "); + switch (ent->ai_family) { + case AF_INET: + dns_b_puts(&dst, "AF_INET"); + break; + case AF_INET6: + dns_b_puts(&dst, "AF_INET6"); + break; + default: + dns_b_fmtju(&dst, ent->ai_family, 0); + break; + } + dns_b_putc(&dst, '\n'); + + dns_b_puts(&dst, ".ai_socktype = "); + switch (ent->ai_socktype) { + case SOCK_STREAM: + dns_b_puts(&dst, "SOCK_STREAM"); + break; + case SOCK_DGRAM: + dns_b_puts(&dst, "SOCK_DGRAM"); + break; + default: + dns_b_fmtju(&dst, ent->ai_socktype, 0); + break; + } + dns_b_putc(&dst, '\n'); + + dns_inet_ntop(dns_sa_family(ent->ai_addr), dns_sa_addr(dns_sa_family(ent->ai_addr), ent->ai_addr, NULL), addr, sizeof addr); + dns_b_puts(&dst, ".ai_addr = ["); + dns_b_puts(&dst, addr); + dns_b_puts(&dst, "]:"); + dns_b_fmtju(&dst, ntohs(*dns_sa_port(dns_sa_family(ent->ai_addr), ent->ai_addr)), 0); + dns_b_putc(&dst, '\n'); + + dns_b_puts(&dst, ".ai_canonname = "); + dns_b_puts(&dst, (ent->ai_canonname)? ent->ai_canonname : "[NULL]"); + dns_b_putc(&dst, '\n'); + + return dns_b_strllen(&dst); +} /* dns_ai_print() */ + + +const struct dns_stat *dns_ai_stat(struct dns_addrinfo *ai) { + return (ai->res)? dns_res_stat(ai->res) : &ai->st; +} /* dns_ai_stat() */ + + +/* + * M I S C E L L A N E O U S R O U T I N E S + * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +static const struct { + char name[16]; + enum dns_section type; +} dns_sections[] = { + { "QUESTION", DNS_S_QUESTION }, + { "QD", DNS_S_QUESTION }, + { "ANSWER", DNS_S_ANSWER }, + { "AN", DNS_S_ANSWER }, + { "AUTHORITY", DNS_S_AUTHORITY }, + { "NS", DNS_S_AUTHORITY }, + { "ADDITIONAL", DNS_S_ADDITIONAL }, + { "AR", DNS_S_ADDITIONAL }, +}; + +const char *(dns_strsection)(enum dns_section section, void *_dst, size_t lim) { + struct dns_buf dst = DNS_B_INTO(_dst, lim); + unsigned i; + + for (i = 0; i < lengthof(dns_sections); i++) { + if (dns_sections[i].type & section) { + dns_b_puts(&dst, dns_sections[i].name); + section &= ~dns_sections[i].type; + if (section) + dns_b_putc(&dst, '|'); + } + } + + if (section || dst.p == dst.base) + dns_b_fmtju(&dst, (0xffff & section), 0); + + return dns_b_tostring(&dst); +} /* dns_strsection() */ + + +enum dns_section dns_isection(const char *src) { + enum dns_section section = 0; + char sbuf[128]; + char *name, *next; + unsigned i; + + dns_strlcpy(sbuf, src, sizeof sbuf); + next = sbuf; + + while ((name = dns_strsep(&next, "|+, \t"))) { + for (i = 0; i < lengthof(dns_sections); i++) { + if (!strcasecmp(dns_sections[i].name, name)) { + section |= dns_sections[i].type; + break; + } + } + } + + return section; +} /* dns_isection() */ + + +static const struct { + char name[8]; + enum dns_class type; +} dns_classes[] = { + { "IN", DNS_C_IN }, +}; + +const char *(dns_strclass)(enum dns_class type, void *_dst, size_t lim) { + struct dns_buf dst = DNS_B_INTO(_dst, lim); + unsigned i; + + for (i = 0; i < lengthof(dns_classes); i++) { + if (dns_classes[i].type == type) { + dns_b_puts(&dst, dns_classes[i].name); + break; + } + } + + if (dst.p == dst.base) + dns_b_fmtju(&dst, (0xffff & type), 0); + + return dns_b_tostring(&dst); +} /* dns_strclass() */ + + +enum dns_class dns_iclass(const char *name) { + unsigned i, class; + + for (i = 0; i < lengthof(dns_classes); i++) { + if (!strcasecmp(dns_classes[i].name, name)) + return dns_classes[i].type; + } + + class = 0; + while (dns_isdigit(*name)) { + class *= 10; + class += *name++ - '0'; + } + + return DNS_PP_MIN(class, 0xffff); +} /* dns_iclass() */ + + +const char *(dns_strtype)(enum dns_type type, void *_dst, size_t lim) { + struct dns_buf dst = DNS_B_INTO(_dst, lim); + unsigned i; + + for (i = 0; i < lengthof(dns_rrtypes); i++) { + if (dns_rrtypes[i].type == type) { + dns_b_puts(&dst, dns_rrtypes[i].name); + break; + } + } + + if (dst.p == dst.base) + dns_b_fmtju(&dst, (0xffff & type), 0); + + return dns_b_tostring(&dst); +} /* dns_strtype() */ + + +enum dns_type dns_itype(const char *name) { + unsigned i, type; + + for (i = 0; i < lengthof(dns_rrtypes); i++) { + if (!strcasecmp(dns_rrtypes[i].name, name)) + return dns_rrtypes[i].type; + } + + type = 0; + while (dns_isdigit(*name)) { + type *= 10; + type += *name++ - '0'; + } + + return DNS_PP_MIN(type, 0xffff); +} /* dns_itype() */ + + +static char dns_opcodes[16][16] = { + [DNS_OP_QUERY] = "QUERY", + [DNS_OP_IQUERY] = "IQUERY", + [DNS_OP_STATUS] = "STATUS", + [DNS_OP_NOTIFY] = "NOTIFY", + [DNS_OP_UPDATE] = "UPDATE", +}; + +static const char *dns__strcode(int code, volatile char *dst, size_t lim) { + char _tmp[48] = ""; + struct dns_buf tmp; + size_t p; + + assert(lim > 0); + dns_b_fmtju(dns_b_into(&tmp, _tmp, DNS_PP_MIN(sizeof _tmp, lim - 1)), code, 0); + + /* copy downwards so first byte is copied last (see below) */ + p = (size_t)(tmp.p - tmp.base); + dst[p] = '\0'; + while (p--) + dst[p] = _tmp[p]; + + return (const char *)dst; +} + +const char *dns_stropcode(enum dns_opcode opcode) { + opcode = (unsigned)opcode % lengthof(dns_opcodes); + + if ('\0' == dns_opcodes[opcode][0]) + return dns__strcode(opcode, dns_opcodes[opcode], sizeof dns_opcodes[opcode]); + + return dns_opcodes[opcode]; +} /* dns_stropcode() */ + + +enum dns_opcode dns_iopcode(const char *name) { + unsigned opcode; + + for (opcode = 0; opcode < lengthof(dns_opcodes); opcode++) { + if (!strcasecmp(name, dns_opcodes[opcode])) + return opcode; + } + + opcode = 0; + while (dns_isdigit(*name)) { + opcode *= 10; + opcode += *name++ - '0'; + } + + return DNS_PP_MIN(opcode, 0x0f); +} /* dns_iopcode() */ + + +static char dns_rcodes[32][16] = { + [DNS_RC_NOERROR] = "NOERROR", + [DNS_RC_FORMERR] = "FORMERR", + [DNS_RC_SERVFAIL] = "SERVFAIL", + [DNS_RC_NXDOMAIN] = "NXDOMAIN", + [DNS_RC_NOTIMP] = "NOTIMP", + [DNS_RC_REFUSED] = "REFUSED", + [DNS_RC_YXDOMAIN] = "YXDOMAIN", + [DNS_RC_YXRRSET] = "YXRRSET", + [DNS_RC_NXRRSET] = "NXRRSET", + [DNS_RC_NOTAUTH] = "NOTAUTH", + [DNS_RC_NOTZONE] = "NOTZONE", + /* EDNS(0) extended RCODEs ... */ + [DNS_RC_BADVERS] = "BADVERS", +}; + +const char *dns_strrcode(enum dns_rcode rcode) { + rcode = (unsigned)rcode % lengthof(dns_rcodes); + + if ('\0' == dns_rcodes[rcode][0]) + return dns__strcode(rcode, dns_rcodes[rcode], sizeof dns_rcodes[rcode]); + + return dns_rcodes[rcode]; +} /* dns_strrcode() */ + + +enum dns_rcode dns_ircode(const char *name) { + unsigned rcode; + + for (rcode = 0; rcode < lengthof(dns_rcodes); rcode++) { + if (!strcasecmp(name, dns_rcodes[rcode])) + return rcode; + } + + rcode = 0; + while (dns_isdigit(*name)) { + rcode *= 10; + rcode += *name++ - '0'; + } + + return DNS_PP_MIN(rcode, 0xfff); +} /* dns_ircode() */ + + + +/* + * C O M M A N D - L I N E / R E G R E S S I O N R O U T I N E S + * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +#if DNS_MAIN + +#include +#include +#include + +#include + +#if _WIN32 +#include +#endif + +#if !_WIN32 +#include +#endif + + +struct { + struct { + const char *path[8]; + unsigned count; + } resconf, nssconf, hosts, cache; + + const char *qname; + enum dns_type qtype; + + int (*sort)(); + + int verbose; +} MAIN = { + .sort = &dns_rr_i_packet, +}; + + +static void hexdump(const unsigned char *src, size_t len, FILE *fp) { + static const unsigned char hex[] = "0123456789abcdef"; + static const unsigned char tmpl[] = " | |\n"; + unsigned char ln[sizeof tmpl]; + const unsigned char *sp, *se; + unsigned char *h, *g; + unsigned i, n; + + sp = src; + se = sp + len; + + while (sp < se) { + memcpy(ln, tmpl, sizeof ln); + + h = &ln[2]; + g = &ln[53]; + + for (n = 0; n < 2; n++) { + for (i = 0; i < 8 && se - sp > 0; i++, sp++) { + h[0] = hex[0x0f & (*sp >> 4)]; + h[1] = hex[0x0f & (*sp >> 0)]; + h += 3; + + *g++ = (isgraph(*sp))? *sp : '.'; + } + + h++; + } + + fputs((char *)ln, fp); + } + + return /* void */; +} /* hexdump() */ + + +DNS_NORETURN static void panic(const char *fmt, ...) { + va_list ap; + + va_start(ap, fmt); + +#if _WIN32 + vfprintf(stderr, fmt, ap); + + exit(EXIT_FAILURE); +#else + verrx(EXIT_FAILURE, fmt, ap); +#endif +} /* panic() */ + +#define panic_(fn, ln, fmt, ...) \ + panic(fmt "%0s", (fn), (ln), __VA_ARGS__) +#define panic(...) \ + panic_(__func__, __LINE__, "(%s:%d) " __VA_ARGS__, "") + + +static void *grow(unsigned char *p, size_t size) { + void *tmp; + + if (!(tmp = realloc(p, size))) + panic("realloc(%"PRIuZ"): %s", size, dns_strerror(errno)); + + return tmp; +} /* grow() */ + + +static size_t add(size_t a, size_t b) { + if (~a < b) + panic("%"PRIuZ" + %"PRIuZ": integer overflow", a, b); + + return a + b; +} /* add() */ + + +static size_t append(unsigned char **dst, size_t osize, const void *src, size_t len) { + size_t size = add(osize, len); + + *dst = grow(*dst, size); + memcpy(*dst + osize, src, len); + + return size; +} /* append() */ + + +static size_t slurp(unsigned char **dst, size_t osize, FILE *fp, const char *path) { + size_t size = osize; + unsigned char buf[1024]; + size_t count; + + while ((count = fread(buf, 1, sizeof buf, fp))) + size = append(dst, size, buf, count); + + if (ferror(fp)) + panic("%s: %s", path, dns_strerror(errno)); + + return size; +} /* slurp() */ + + +static struct dns_resolv_conf *resconf(void) { + static struct dns_resolv_conf *resconf; + const char *path; + unsigned i; + int error; + + if (resconf) + return resconf; + + if (!(resconf = dns_resconf_open(&error))) + panic("dns_resconf_open: %s", dns_strerror(error)); + + if (!MAIN.resconf.count) + MAIN.resconf.path[MAIN.resconf.count++] = "/etc/resolv.conf"; + + for (i = 0; i < MAIN.resconf.count; i++) { + path = MAIN.resconf.path[i]; + + if (0 == strcmp(path, "-")) + error = dns_resconf_loadfile(resconf, stdin); + else + error = dns_resconf_loadpath(resconf, path); + + if (error) + panic("%s: %s", path, dns_strerror(error)); + } + + for (i = 0; i < MAIN.nssconf.count; i++) { + path = MAIN.nssconf.path[i]; + + if (0 == strcmp(path, "-")) + error = dns_nssconf_loadfile(resconf, stdin); + else + error = dns_nssconf_loadpath(resconf, path); + + if (error) + panic("%s: %s", path, dns_strerror(error)); + } + + if (!MAIN.nssconf.count) { + path = "/etc/nsswitch.conf"; + + if (!(error = dns_nssconf_loadpath(resconf, path))) + MAIN.nssconf.path[MAIN.nssconf.count++] = path; + else if (error != ENOENT) + panic("%s: %s", path, dns_strerror(error)); + } + + return resconf; +} /* resconf() */ + + +static struct dns_hosts *hosts(void) { + static struct dns_hosts *hosts; + const char *path; + unsigned i; + int error; + + if (hosts) + return hosts; + + if (!MAIN.hosts.count) { + MAIN.hosts.path[MAIN.hosts.count++] = "/etc/hosts"; + + /* Explicitly test dns_hosts_local() */ + if (!(hosts = dns_hosts_local(&error))) + panic("%s: %s", "/etc/hosts", dns_strerror(error)); + + return hosts; + } + + if (!(hosts = dns_hosts_open(&error))) + panic("dns_hosts_open: %s", dns_strerror(error)); + + for (i = 0; i < MAIN.hosts.count; i++) { + path = MAIN.hosts.path[i]; + + if (0 == strcmp(path, "-")) + error = dns_hosts_loadfile(hosts, stdin); + else + error = dns_hosts_loadpath(hosts, path); + + if (error) + panic("%s: %s", path, dns_strerror(error)); + } + + return hosts; +} /* hosts() */ + + +#if DNS_CACHE +#include "cache.h" + +static struct dns_cache *cache(void) { + static struct cache *cache; + const char *path; + unsigned i; + int error; + + if (cache) + return cache_resi(cache); + if (!MAIN.cache.count) + return NULL; + + if (!(cache = cache_open(&error))) + panic("%s: %s", MAIN.cache.path[0], dns_strerror(error)); + + for (i = 0; i < MAIN.cache.count; i++) { + path = MAIN.cache.path[i]; + + if (!strcmp(path, "-")) { + if ((error = cache_loadfile(cache, stdin, NULL, 0))) + panic("%s: %s", path, dns_strerror(error)); + } else if ((error = cache_loadpath(cache, path, NULL, 0))) + panic("%s: %s", path, dns_strerror(error)); + } + + return cache_resi(cache); +} /* cache() */ +#else +static struct dns_cache *cache(void) { return NULL; } +#endif + + +static void print_packet(struct dns_packet *P, FILE *fp) { + dns_p_dump3(P, dns_rr_i_new(P, .sort = MAIN.sort), fp); + + if (MAIN.verbose > 2) + hexdump(P->data, P->end, fp); +} /* print_packet() */ + + +static int parse_packet(int argc DNS_NOTUSED, char *argv[] DNS_NOTUSED) { + struct dns_packet *P = dns_p_new(512); + struct dns_packet *Q = dns_p_new(512); + enum dns_section section; + struct dns_rr rr; + int error; + union dns_any any; + char pretty[sizeof any * 2]; + size_t len; + + P->end = fread(P->data, 1, P->size, stdin); + + fputs(";; [HEADER]\n", stdout); + fprintf(stdout, ";; qr : %s(%d)\n", (dns_header(P)->qr)? "RESPONSE" : "QUERY", dns_header(P)->qr); + fprintf(stdout, ";; opcode : %s(%d)\n", dns_stropcode(dns_header(P)->opcode), dns_header(P)->opcode); + fprintf(stdout, ";; aa : %s(%d)\n", (dns_header(P)->aa)? "AUTHORITATIVE" : "NON-AUTHORITATIVE", dns_header(P)->aa); + fprintf(stdout, ";; tc : %s(%d)\n", (dns_header(P)->tc)? "TRUNCATED" : "NOT-TRUNCATED", dns_header(P)->tc); + fprintf(stdout, ";; rd : %s(%d)\n", (dns_header(P)->rd)? "RECURSION-DESIRED" : "RECURSION-NOT-DESIRED", dns_header(P)->rd); + fprintf(stdout, ";; ra : %s(%d)\n", (dns_header(P)->ra)? "RECURSION-ALLOWED" : "RECURSION-NOT-ALLOWED", dns_header(P)->ra); + fprintf(stdout, ";; rcode : %s(%d)\n", dns_strrcode(dns_p_rcode(P)), dns_p_rcode(P)); + + section = 0; + + dns_rr_foreach(&rr, P, .sort = MAIN.sort) { + if (section != rr.section) + fprintf(stdout, "\n;; [%s:%d]\n", dns_strsection(rr.section), dns_p_count(P, rr.section)); + + if ((len = dns_rr_print(pretty, sizeof pretty, &rr, P, &error))) + fprintf(stdout, "%s\n", pretty); + + dns_rr_copy(Q, &rr, P); + + section = rr.section; + } + + fputs("; ; ; ; ; ; ; ;\n\n", stdout); + + section = 0; + +#if 0 + dns_rr_foreach(&rr, Q, .name = "ns8.yahoo.com.") { +#else + struct dns_rr rrset[32]; + struct dns_rr_i *rri = dns_rr_i_new(Q, .name = dns_d_new("ns8.yahoo.com", DNS_D_ANCHOR), .sort = MAIN.sort); + unsigned rrcount = dns_rr_grep(rrset, lengthof(rrset), rri, Q, &error); + + for (unsigned i = 0; i < rrcount; i++) { + rr = rrset[i]; +#endif + if (section != rr.section) + fprintf(stdout, "\n;; [%s:%d]\n", dns_strsection(rr.section), dns_p_count(Q, rr.section)); + + if ((len = dns_rr_print(pretty, sizeof pretty, &rr, Q, &error))) + fprintf(stdout, "%s\n", pretty); + + section = rr.section; + } + + if (MAIN.verbose > 1) { + fprintf(stderr, "orig:%"PRIuZ"\n", P->end); + hexdump(P->data, P->end, stdout); + + fprintf(stderr, "copy:%"PRIuZ"\n", Q->end); + hexdump(Q->data, Q->end, stdout); + } + + return 0; +} /* parse_packet() */ + + +static int parse_domain(int argc, char *argv[]) { + char *dn; + + dn = (argc > 1)? argv[1] : "f.l.google.com"; + + printf("[%s]\n", dn); + + dn = dns_d_new(dn); + + do { + puts(dn); + } while (dns_d_cleave(dn, strlen(dn) + 1, dn, strlen(dn))); + + return 0; +} /* parse_domain() */ + + +static int trim_domain(int argc, char **argv) { + for (argc--, argv++; argc > 0; argc--, argv++) { + char name[DNS_D_MAXNAME + 1]; + + dns_d_trim(name, sizeof name, *argv, strlen(*argv), DNS_D_ANCHOR); + + puts(name); + } + + return 0; +} /* trim_domain() */ + + +static int expand_domain(int argc, char *argv[]) { + unsigned short rp = 0; + unsigned char *src = NULL; + unsigned char *dst; + struct dns_packet *pkt; + size_t lim = 0, len; + int error; + + if (argc > 1) + rp = atoi(argv[1]); + + len = slurp(&src, 0, stdin, "-"); + + if (!(pkt = dns_p_make(len, &error))) + panic("malloc(%"PRIuZ"): %s", len, dns_strerror(error)); + + memcpy(pkt->data, src, len); + pkt->end = len; + + lim = 1; + dst = grow(NULL, lim); + + while (lim <= (len = dns_d_expand(dst, lim, rp, pkt, &error))) { + lim = add(len, 1); + dst = grow(dst, lim); + } + + if (!len) + panic("expand: %s", dns_strerror(error)); + + fwrite(dst, 1, len, stdout); + fflush(stdout); + + free(src); + free(dst); + free(pkt); + + return 0; +} /* expand_domain() */ + + +static int show_resconf(int argc DNS_NOTUSED, char *argv[] DNS_NOTUSED) { + unsigned i; + + resconf(); /* load it */ + + fputs("; SOURCES\n", stdout); + + for (i = 0; i < MAIN.resconf.count; i++) + fprintf(stdout, "; %s\n", MAIN.resconf.path[i]); + + for (i = 0; i < MAIN.nssconf.count; i++) + fprintf(stdout, "; %s\n", MAIN.nssconf.path[i]); + + fputs(";\n", stdout); + + dns_resconf_dump(resconf(), stdout); + + return 0; +} /* show_resconf() */ + + +static int show_nssconf(int argc DNS_NOTUSED, char *argv[] DNS_NOTUSED) { + unsigned i; + + resconf(); + + fputs("# SOURCES\n", stdout); + + for (i = 0; i < MAIN.resconf.count; i++) + fprintf(stdout, "# %s\n", MAIN.resconf.path[i]); + + for (i = 0; i < MAIN.nssconf.count; i++) + fprintf(stdout, "# %s\n", MAIN.nssconf.path[i]); + + fputs("#\n", stdout); + + dns_nssconf_dump(resconf(), stdout); + + return 0; +} /* show_nssconf() */ + + +static int show_hosts(int argc DNS_NOTUSED, char *argv[] DNS_NOTUSED) { + unsigned i; + + hosts(); + + fputs("# SOURCES\n", stdout); + + for (i = 0; i < MAIN.hosts.count; i++) + fprintf(stdout, "# %s\n", MAIN.hosts.path[i]); + + fputs("#\n", stdout); + + dns_hosts_dump(hosts(), stdout); + + return 0; +} /* show_hosts() */ + + +static int query_hosts(int argc, char *argv[]) { + struct dns_packet *Q = dns_p_new(512); + struct dns_packet *A; + char qname[DNS_D_MAXNAME + 1]; + size_t qlen; + int error; + + if (!MAIN.qname) + MAIN.qname = (argc > 1)? argv[1] : "localhost"; + if (!MAIN.qtype) + MAIN.qtype = DNS_T_A; + + hosts(); + + if (MAIN.qtype == DNS_T_PTR && !strstr(MAIN.qname, "arpa")) { + union { struct in_addr a; struct in6_addr a6; } addr; + int af = (strchr(MAIN.qname, ':'))? AF_INET6 : AF_INET; + + if ((error = dns_pton(af, MAIN.qname, &addr))) + panic("%s: %s", MAIN.qname, dns_strerror(error)); + + qlen = dns_ptr_qname(qname, sizeof qname, af, &addr); + } else + qlen = dns_strlcpy(qname, MAIN.qname, sizeof qname); + + if ((error = dns_p_push(Q, DNS_S_QD, qname, qlen, MAIN.qtype, DNS_C_IN, 0, 0))) + panic("%s: %s", qname, dns_strerror(error)); + + if (!(A = dns_hosts_query(hosts(), Q, &error))) + panic("%s: %s", qname, dns_strerror(error)); + + print_packet(A, stdout); + + free(A); + + return 0; +} /* query_hosts() */ + + +static int search_list(int argc, char *argv[]) { + const char *qname = (argc > 1)? argv[1] : "f.l.google.com"; + unsigned long i = 0; + char name[DNS_D_MAXNAME + 1]; + + printf("[%s]\n", qname); + + while (dns_resconf_search(name, sizeof name, qname, strlen(qname), resconf(), &i)) + puts(name); + + return 0; +} /* search_list() */ + + +static int permute_set(int argc, char *argv[]) { + unsigned lo, hi, i; + struct dns_k_permutor p; + + hi = (--argc > 0)? atoi(argv[argc]) : 8; + lo = (--argc > 0)? atoi(argv[argc]) : 0; + + fprintf(stderr, "[%u .. %u]\n", lo, hi); + + dns_k_permutor_init(&p, lo, hi); + + for (i = lo; i <= hi; i++) + fprintf(stdout, "%u\n", dns_k_permutor_step(&p)); +// printf("%u -> %u -> %u\n", i, dns_k_permutor_E(&p, i), dns_k_permutor_D(&p, dns_k_permutor_E(&p, i))); + + return 0; +} /* permute_set() */ + + +static int shuffle_16(int argc, char *argv[]) { + unsigned n, r; + + if (--argc > 0) { + n = 0xffff & atoi(argv[argc]); + r = (--argc > 0)? (unsigned)atoi(argv[argc]) : dns_random(); + + fprintf(stdout, "%hu\n", dns_k_shuffle16(n, r)); + } else { + r = dns_random(); + + for (n = 0; n < 65536; n++) + fprintf(stdout, "%hu\n", dns_k_shuffle16(n, r)); + } + + return 0; +} /* shuffle_16() */ + + +static int dump_random(int argc, char *argv[]) { + unsigned char b[32]; + unsigned i, j, n, r; + + n = (argc > 1)? atoi(argv[1]) : 32; + + while (n) { + i = 0; + + do { + r = dns_random(); + + for (j = 0; j < sizeof r && i < n && i < sizeof b; i++, j++) { + b[i] = 0xff & r; + r >>= 8; + } + } while (i < n && i < sizeof b); + + hexdump(b, i, stdout); + + n -= i; + } + + return 0; +} /* dump_random() */ + + +static int send_query(int argc, char *argv[]) { + struct dns_packet *A, *Q = dns_p_new(512); + char host[INET6_ADDRSTRLEN + 1]; + struct sockaddr_storage ss; + struct dns_socket *so; + int error, type; + + if (argc > 1) { + ss.ss_family = (strchr(argv[1], ':'))? AF_INET6 : AF_INET; + + if ((error = dns_pton(ss.ss_family, argv[1], dns_sa_addr(ss.ss_family, &ss, NULL)))) + panic("%s: %s", argv[1], dns_strerror(error)); + + *dns_sa_port(ss.ss_family, &ss) = htons(53); + } else + memcpy(&ss, &resconf()->nameserver[0], dns_sa_len(&resconf()->nameserver[0])); + + if (!dns_inet_ntop(ss.ss_family, dns_sa_addr(ss.ss_family, &ss, NULL), host, sizeof host)) + panic("bad host address, or none provided"); + + if (!MAIN.qname) + MAIN.qname = "ipv6.google.com"; + if (!MAIN.qtype) + MAIN.qtype = DNS_T_AAAA; + + if ((error = dns_p_push(Q, DNS_S_QD, MAIN.qname, strlen(MAIN.qname), MAIN.qtype, DNS_C_IN, 0, 0))) + panic("dns_p_push: %s", dns_strerror(error)); + + dns_header(Q)->rd = 1; + + if (strstr(argv[0], "udp")) + type = SOCK_DGRAM; + else if (strstr(argv[0], "tcp")) + type = SOCK_STREAM; + else + type = dns_res_tcp2type(resconf()->options.tcp); + + fprintf(stderr, "querying %s for %s IN %s\n", host, MAIN.qname, dns_strtype(MAIN.qtype)); + + if (!(so = dns_so_open((struct sockaddr *)&resconf()->iface, type, dns_opts(), &error))) + panic("dns_so_open: %s", dns_strerror(error)); + + while (!(A = dns_so_query(so, Q, (struct sockaddr *)&ss, &error))) { + if (error != DNS_EAGAIN) + panic("dns_so_query: %s (%d)", dns_strerror(error), error); + if (dns_so_elapsed(so) > 10) + panic("query timed-out"); + + dns_so_poll(so, 1); + } + + print_packet(A, stdout); + + dns_so_close(so); + + return 0; +} /* send_query() */ + + +static int print_arpa(int argc, char *argv[]) { + const char *ip = (argc > 1)? argv[1] : "::1"; + int af = (strchr(ip, ':'))? AF_INET6 : AF_INET; + union { struct in_addr a4; struct in6_addr a6; } addr; + char host[DNS_D_MAXNAME + 1]; + + if (1 != dns_inet_pton(af, ip, &addr) || 0 == dns_ptr_qname(host, sizeof host, af, &addr)) + panic("%s: invalid address", ip); + + fprintf(stdout, "%s\n", host); + + return 0; +} /* print_arpa() */ + + +static int show_hints(int argc, char *argv[]) { + struct dns_hints *(*load)(struct dns_resolv_conf *, int *); + const char *which, *how, *who; + struct dns_hints *hints; + int error; + + which = (argc > 1)? argv[1] : "local"; + how = (argc > 2)? argv[2] : "plain"; + who = (argc > 3)? argv[3] : "google.com"; + + load = (0 == strcmp(which, "local")) + ? &dns_hints_local + : &dns_hints_root; + + if (!(hints = load(resconf(), &error))) + panic("%s: %s", argv[0], dns_strerror(error)); + + if (0 == strcmp(how, "plain")) { + dns_hints_dump(hints, stdout); + } else { + struct dns_packet *query, *answer; + + query = dns_p_new(512); + + if ((error = dns_p_push(query, DNS_S_QUESTION, who, strlen(who), DNS_T_A, DNS_C_IN, 0, 0))) + panic("%s: %s", who, dns_strerror(error)); + + if (!(answer = dns_hints_query(hints, query, &error))) + panic("%s: %s", who, dns_strerror(error)); + + print_packet(answer, stdout); + + free(answer); + } + + dns_hints_close(hints); + + return 0; +} /* show_hints() */ + + +static int resolve_query(int argc DNS_NOTUSED, char *argv[]) { + _Bool recurse = !!strstr(argv[0], "recurse"); + struct dns_hints *(*hints)() = (recurse)? &dns_hints_root : &dns_hints_local; + struct dns_resolver *R; + struct dns_packet *ans; + const struct dns_stat *st; + int error; + + if (!MAIN.qname) + MAIN.qname = "www.google.com"; + if (!MAIN.qtype) + MAIN.qtype = DNS_T_A; + + resconf()->options.recurse = recurse; + + if (!(R = dns_res_open(resconf(), hosts(), dns_hints_mortal(hints(resconf(), &error)), cache(), dns_opts(), &error))) + panic("%s: %s", MAIN.qname, dns_strerror(error)); + + if ((error = dns_res_submit(R, MAIN.qname, MAIN.qtype, DNS_C_IN))) + panic("%s: %s", MAIN.qname, dns_strerror(error)); + + while ((error = dns_res_check(R))) { + if (error != DNS_EAGAIN) + panic("dns_res_check: %s (%d)", dns_strerror(error), error); + if (dns_res_elapsed(R) > 30) + panic("query timed-out"); + + dns_res_poll(R, 1); + } + + ans = dns_res_fetch(R, &error); + print_packet(ans, stdout); + free(ans); + + st = dns_res_stat(R); + putchar('\n'); + printf(";; queries: %"PRIuZ"\n", st->queries); + printf(";; udp sent: %"PRIuZ" in %"PRIuZ" bytes\n", st->udp.sent.count, st->udp.sent.bytes); + printf(";; udp rcvd: %"PRIuZ" in %"PRIuZ" bytes\n", st->udp.rcvd.count, st->udp.rcvd.bytes); + printf(";; tcp sent: %"PRIuZ" in %"PRIuZ" bytes\n", st->tcp.sent.count, st->tcp.sent.bytes); + printf(";; tcp rcvd: %"PRIuZ" in %"PRIuZ" bytes\n", st->tcp.rcvd.count, st->tcp.rcvd.bytes); + + dns_res_close(R); + + return 0; +} /* resolve_query() */ + + +static int resolve_addrinfo(int argc DNS_NOTUSED, char *argv[]) { + _Bool recurse = !!strstr(argv[0], "recurse"); + struct dns_hints *(*hints)() = (recurse)? &dns_hints_root : &dns_hints_local; + struct dns_resolver *res = NULL; + struct dns_addrinfo *ai = NULL; + struct addrinfo ai_hints = { .ai_family = PF_UNSPEC, .ai_socktype = SOCK_STREAM, .ai_flags = AI_CANONNAME }; + struct addrinfo *ent; + char pretty[512]; + int error; + + if (!MAIN.qname) + MAIN.qname = "www.google.com"; + /* NB: MAIN.qtype of 0 means obey hints.ai_family */ + + resconf()->options.recurse = recurse; + + if (!(res = dns_res_open(resconf(), hosts(), dns_hints_mortal(hints(resconf(), &error)), cache(), dns_opts(), &error))) + panic("%s: %s", MAIN.qname, dns_strerror(error)); + + if (!(ai = dns_ai_open(MAIN.qname, "80", MAIN.qtype, &ai_hints, res, &error))) + panic("%s: %s", MAIN.qname, dns_strerror(error)); + + do { + switch (error = dns_ai_nextent(&ent, ai)) { + case 0: + dns_ai_print(pretty, sizeof pretty, ent, ai); + + fputs(pretty, stdout); + + free(ent); + + break; + case ENOENT: + break; + case DNS_EAGAIN: + if (dns_ai_elapsed(ai) > 30) + panic("query timed-out"); + + dns_ai_poll(ai, 1); + + break; + default: + panic("dns_ai_nextent: %s (%d)", dns_strerror(error), error); + } + } while (error != ENOENT); + + dns_res_close(res); + dns_ai_close(ai); + + return 0; +} /* resolve_addrinfo() */ + + +static int echo_port(int argc DNS_NOTUSED, char *argv[] DNS_NOTUSED) { + union { + struct sockaddr sa; + struct sockaddr_in sin; + } port; + int fd; + + memset(&port, 0, sizeof port); + port.sin.sin_family = AF_INET; + port.sin.sin_port = htons(5354); + port.sin.sin_addr.s_addr = inet_addr("127.0.0.1"); + + if (-1 == (fd = socket(PF_INET, SOCK_DGRAM, 0))) + panic("socket: %s", strerror(errno)); + + if (0 != bind(fd, &port.sa, sizeof port.sa)) + panic("127.0.0.1:5353: %s", dns_strerror(errno)); + + for (;;) { + struct dns_packet *pkt = dns_p_new(512); + struct sockaddr_storage ss; + socklen_t slen = sizeof ss; + ssize_t count; +#if defined(MSG_WAITALL) /* MinGW issue */ + int rflags = MSG_WAITALL; +#else + int rflags = 0; +#endif + + count = recvfrom(fd, (char *)pkt->data, pkt->size, rflags, (struct sockaddr *)&ss, &slen); + + if (!count || count < 0) + panic("recvfrom: %s", strerror(errno)); + + pkt->end = count; + + dns_p_dump(pkt, stdout); + + (void)sendto(fd, (char *)pkt->data, pkt->end, 0, (struct sockaddr *)&ss, slen); + } + + return 0; +} /* echo_port() */ + + +static int isection(int argc, char *argv[]) { + const char *name = (argc > 1)? argv[1] : ""; + int type; + + type = dns_isection(name); + name = dns_strsection(type); + + printf("%s (%d)\n", name, type); + + return 0; +} /* isection() */ + + +static int iclass(int argc, char *argv[]) { + const char *name = (argc > 1)? argv[1] : ""; + int type; + + type = dns_iclass(name); + name = dns_strclass(type); + + printf("%s (%d)\n", name, type); + + return 0; +} /* iclass() */ + + +static int itype(int argc, char *argv[]) { + const char *name = (argc > 1)? argv[1] : ""; + int type; + + type = dns_itype(name); + name = dns_strtype(type); + + printf("%s (%d)\n", name, type); + + return 0; +} /* itype() */ + + +static int iopcode(int argc, char *argv[]) { + const char *name = (argc > 1)? argv[1] : ""; + int type; + + type = dns_iopcode(name); + name = dns_stropcode(type); + + printf("%s (%d)\n", name, type); + + return 0; +} /* iopcode() */ + + +static int ircode(int argc, char *argv[]) { + const char *name = (argc > 1)? argv[1] : ""; + int type; + + type = dns_ircode(name); + name = dns_strrcode(type); + + printf("%s (%d)\n", name, type); + + return 0; +} /* ircode() */ + + +#define SIZE1(x) { DNS_PP_STRINGIFY(x), sizeof (x) } +#define SIZE2(x, ...) SIZE1(x), SIZE1(__VA_ARGS__) +#define SIZE3(x, ...) SIZE1(x), SIZE2(__VA_ARGS__) +#define SIZE4(x, ...) SIZE1(x), SIZE3(__VA_ARGS__) +#define SIZE(...) DNS_PP_CALL(DNS_PP_XPASTE(SIZE, DNS_PP_NARG(__VA_ARGS__)), __VA_ARGS__) + +static int sizes(int argc DNS_NOTUSED, char *argv[] DNS_NOTUSED) { + static const struct { const char *name; size_t size; } type[] = { + SIZE(struct dns_header, struct dns_packet, struct dns_rr, struct dns_rr_i), + SIZE(struct dns_a, struct dns_aaaa, struct dns_mx, struct dns_ns), + SIZE(struct dns_cname, struct dns_soa, struct dns_ptr, struct dns_srv), + SIZE(struct dns_sshfp, struct dns_txt, union dns_any), + SIZE(struct dns_resolv_conf, struct dns_hosts, struct dns_hints, struct dns_hints_i), + SIZE(struct dns_options, struct dns_socket, struct dns_resolver, struct dns_addrinfo), + SIZE(struct dns_cache), SIZE(size_t), SIZE(void *), SIZE(long) + }; + unsigned i, max; + + for (i = 0, max = 0; i < lengthof(type); i++) + max = DNS_PP_MAX(max, strlen(type[i].name)); + + for (i = 0; i < lengthof(type); i++) + printf("%*s : %"PRIuZ"\n", max, type[i].name, type[i].size); + + return 0; +} /* sizes() */ + + +static const struct { const char *cmd; int (*run)(); const char *help; } cmds[] = { + { "parse-packet", &parse_packet, "parse binary packet from stdin" }, + { "parse-domain", &parse_domain, "anchor and iteratively cleave domain" }, + { "trim-domain", &trim_domain, "trim and anchor domain name" }, + { "expand-domain", &expand_domain, "expand domain at offset NN in packet from stdin" }, + { "show-resconf", &show_resconf, "show resolv.conf data" }, + { "show-hosts", &show_hosts, "show hosts data" }, + { "show-nssconf", &show_nssconf, "show nsswitch.conf data" }, + { "query-hosts", &query_hosts, "query A, AAAA or PTR in hosts data" }, + { "search-list", &search_list, "generate query search list from domain" }, + { "permute-set", &permute_set, "generate random permutation -> (0 .. N or N .. M)" }, + { "shuffle-16", &shuffle_16, "simple 16-bit permutation" }, + { "dump-random", &dump_random, "generate random bytes" }, + { "send-query", &send_query, "send query to host" }, + { "send-query-udp", &send_query, "send udp query to host" }, + { "send-query-tcp", &send_query, "send tcp query to host" }, + { "print-arpa", &print_arpa, "print arpa. zone name of address" }, + { "show-hints", &show_hints, "print hints: show-hints [local|root] [plain|packet]" }, + { "resolve-stub", &resolve_query, "resolve as stub resolver" }, + { "resolve-recurse", &resolve_query, "resolve as recursive resolver" }, + { "addrinfo-stub", &resolve_addrinfo, "resolve through getaddrinfo clone" }, + { "addrinfo-recurse", &resolve_addrinfo, "resolve through getaddrinfo clone" }, +/* { "resolve-nameinfo", &resolve_query, "resolve as recursive resolver" }, */ + { "echo", &echo_port, "server echo mode, for nmap fuzzing" }, + { "isection", &isection, "parse section string" }, + { "iclass", &iclass, "parse class string" }, + { "itype", &itype, "parse type string" }, + { "iopcode", &iopcode, "parse opcode string" }, + { "ircode", &ircode, "parse rcode string" }, + { "sizes", &sizes, "print data structure sizes" }, +}; + + +static void print_usage(const char *progname, FILE *fp) { + static const char *usage = + " [OPTIONS] COMMAND [ARGS]\n" + " -c PATH Path to resolv.conf\n" + " -n PATH Path to nsswitch.conf\n" + " -l PATH Path to local hosts\n" + " -z PATH Path to zone cache\n" + " -q QNAME Query name\n" + " -t QTYPE Query type\n" + " -s HOW Sort records\n" + " -v Be more verbose (-vv show packets; -vvv hexdump packets)\n" + " -V Print version info\n" + " -h Print this usage message\n" + "\n"; + unsigned i, n, m; + + fputs(progname, fp); + fputs(usage, fp); + + for (i = 0, m = 0; i < lengthof(cmds); i++) { + if (strlen(cmds[i].cmd) > m) + m = strlen(cmds[i].cmd); + } + + for (i = 0; i < lengthof(cmds); i++) { + fprintf(fp, " %s ", cmds[i].cmd); + + for (n = strlen(cmds[i].cmd); n < m; n++) + putc(' ', fp); + + fputs(cmds[i].help, fp); + putc('\n', fp); + } + + fputs("\nReport bugs to William Ahern \n", fp); +} /* print_usage() */ + + +static void print_version(const char *progname, FILE *fp) { + fprintf(fp, "%s (dns.c) %.8X\n", progname, dns_v_rel()); + fprintf(fp, "vendor %s\n", dns_vendor()); + fprintf(fp, "release %.8X\n", dns_v_rel()); + fprintf(fp, "abi %.8X\n", dns_v_abi()); + fprintf(fp, "api %.8X\n", dns_v_api()); +} /* print_version() */ + + +int main(int argc, char **argv) { + extern int optind; + extern char *optarg; + const char *progname = argv[0]; + unsigned i; + int ch; + + while (-1 != (ch = getopt(argc, argv, "q:t:c:n:l:z:s:vVh"))) { + switch (ch) { + case 'c': + assert(MAIN.resconf.count < lengthof(MAIN.resconf.path)); + + MAIN.resconf.path[MAIN.resconf.count++] = optarg; + + break; + case 'n': + assert(MAIN.nssconf.count < lengthof(MAIN.nssconf.path)); + + MAIN.nssconf.path[MAIN.nssconf.count++] = optarg; + + break; + case 'l': + assert(MAIN.hosts.count < lengthof(MAIN.hosts.path)); + + MAIN.hosts.path[MAIN.hosts.count++] = optarg; + + break; + case 'z': + assert(MAIN.cache.count < lengthof(MAIN.cache.path)); + + MAIN.cache.path[MAIN.cache.count++] = optarg; + + break; + case 'q': + MAIN.qname = optarg; + + break; + case 't': + for (i = 0; i < lengthof(dns_rrtypes); i++) { + if (0 == strcasecmp(dns_rrtypes[i].name, optarg)) + { MAIN.qtype = dns_rrtypes[i].type; break; } + } + + if (MAIN.qtype) + break; + + for (i = 0; dns_isdigit(optarg[i]); i++) { + MAIN.qtype *= 10; + MAIN.qtype += optarg[i] - '0'; + } + + if (!MAIN.qtype) + panic("%s: invalid query type", optarg); + + break; + case 's': + if (0 == strcasecmp(optarg, "packet")) + MAIN.sort = &dns_rr_i_packet; + else if (0 == strcasecmp(optarg, "shuffle")) + MAIN.sort = &dns_rr_i_shuffle; + else if (0 == strcasecmp(optarg, "order")) + MAIN.sort = &dns_rr_i_order; + else + panic("%s: invalid sort method", optarg); + + break; + case 'v': + dns_debug = ++MAIN.verbose; + + break; + case 'V': + print_version(progname, stdout); + + return 0; + case 'h': + print_usage(progname, stdout); + + return 0; + default: + print_usage(progname, stderr); + + return EXIT_FAILURE; + } /* switch() */ + } /* while() */ + + argc -= optind; + argv += optind; + + for (i = 0; i < lengthof(cmds) && argv[0]; i++) { + if (0 == strcmp(cmds[i].cmd, argv[0])) + return cmds[i].run(argc, argv); + } + + print_usage(progname, stderr); + + return EXIT_FAILURE; +} /* main() */ + + +#endif /* DNS_MAIN */ + + +/* + * pop file-scoped compiler annotations + */ +#if __clang__ +#pragma clang diagnostic pop +#elif DNS_GNUC_PREREQ(4,6,0) +#pragma GCC diagnostic pop +#endif + diff --git a/src/generic/dns_query/dns_query.c b/src/generic/dns_query/dns_query.c new file mode 100644 index 00000000..937d265c --- /dev/null +++ b/src/generic/dns_query/dns_query.c @@ -0,0 +1,243 @@ +/** + * Copyright (C) 2016 Fraunhofer FKIE + * + * @author Henning Rogge + */ + +/** + * @file + */ + +#include +#include + +#include +#include +#include + +#include +#include +#include + +#include +#include + +#define LOG_DNS_QUERY _dns_query_subsystem.logging + +struct _dns_query_config { + uint64_t timeout; +}; + +static int _init(void); +static void _cleanup(void); + +static void _cb_process_dns_query(struct oonf_socket_entry *entry); +static void _cb_dns_timeout(struct oonf_timer_instance *timer); +static void _cb_config_changed(void); + +/* timeouts */ +static struct oonf_timer_class _dns_timeout = { + .name = "dns query timeout", + .callback = _cb_dns_timeout +}; + +/* configuration */ +static struct cfg_schema_entry _dns_query_entries[] = { + CFG_MAP_CLOCK(_dns_query_config, timeout, "timeout", "1.0", "Default DNS query timeout"), +}; + +static struct cfg_schema_section _dns_query_section = { + .type = OONF_DNS_QUERY_SUBSYSTEM, + .cb_delta_handler = _cb_config_changed, + .entries = _dns_query_entries, + .entry_count = ARRAYSIZE(_dns_query_entries), +}; + +static struct _dns_query_config _config; + +/* plugin declaration */ +static const char *_dependencies[] = { + OONF_TIMER_SUBSYSTEM, + OONF_SOCKET_SUBSYSTEM, +}; +static struct oonf_subsystem _dns_query_subsystem = { + .name = OONF_DNS_QUERY_SUBSYSTEM, + .dependencies = _dependencies, + .dependencies_count = ARRAYSIZE(_dependencies), + .descr = "OONF dns query plugin", + .author = "Henning Rogge", + + .cfg_section = &_dns_query_section, + + .init = _init, + .cleanup = _cleanup, +}; +DECLARE_OONF_PLUGIN(_dns_query_subsystem); + +static int +_init(void) { + oonf_timer_add(&_dns_timeout); + return 0; +} + +static void +_cleanup(void) { + oonf_timer_remove(&_dns_timeout); +} + +int +dns_query_do(struct oonf_dns_query *q) { + int error; + + if (q->timeout == 0) { + /* use default timeout */ + q->timeout = _config.timeout; + } + if (!q->socket_name) { + q->socket_name = q->query; + } + + dns_p_init(&q->_bin_query.packet, sizeof(q->_bin_query)); + if ((error = dns_p_push(&q->_bin_query.packet, + DNS_S_QD, q->query, strlen(q->query), q->dns_type, DNS_C_IN, 0, 0))) { + OONF_WARN(LOG_DNS_QUERY, "Could not generate DNS query '%s': %s (%d)", + q->query, dns_strerror(error), error); + return error; + } + dns_header(&q->_bin_query.packet)->rd = 1; + + /* initialize new DNS query */ + if (!(q->_dns_socket = dns_so_open(&q->dns_client->std, SOCK_DGRAM, dns_opts(), &error))) { + OONF_WARN(LOG_DNS_QUERY, "Could not open DNS client socket: %s (%d)", dns_strerror(error), error); + return error; + } + + /* initiatize socket handling */ + q->_socket_entry.name = q->socket_name; + q->_socket_entry.process = _cb_process_dns_query; + os_fd_invalidate(&q->_socket_entry.fd); + + /* initialize timeout */ + q->_timeout.class = &_dns_timeout; + oonf_timer_start(&q->_timeout, q->timeout); + + /* run first stage DNS query handling */ + _cb_process_dns_query(&q->_socket_entry); + + return 0; +} + +static void +_cb_process_dns_query(struct oonf_socket_entry *entry) { + struct oonf_dns_query *q; + struct dns_packet *A; + struct dns_rr rr; + union dns_any any; + int error, events; + bool specific_callback; + char buffer[256]; + q = container_of(entry, struct oonf_dns_query, _socket_entry); + + /* reset timeout */ + oonf_timer_set(&q->_timeout, q->timeout); + + OONF_DEBUG(LOG_DNS_QUERY, "continue DNS query %s", q->query); + A = dns_so_query(q->_dns_socket, &q->_bin_query.packet, &q->dns_server->std, &error); + if (!A) { + if (error != EAGAIN) { + OONF_WARN(LOG_DNS_QUERY, "Error while progressing DNS query: %s (%d)", + dns_strerror(error), error); + } + events = dns_so_events(q->_dns_socket); + if (!os_fd_is_initialized(&q->_socket_entry.fd)) { + /* lazy initialization */ + os_fd_init(&q->_socket_entry.fd, dns_so_pollfd(q->_dns_socket)); + oonf_socket_add(&q->_socket_entry); + } + oonf_socket_set_read(&q->_socket_entry, (events & DNS_POLLIN) == DNS_POLLIN); + oonf_socket_set_write(&q->_socket_entry, (events & DNS_POLLOUT) == DNS_POLLOUT); + OONF_DEBUG(LOG_DNS_QUERY, "Wait for socket event (in=%s, out=%s)", + (events & DNS_POLLIN) == DNS_POLLIN ? "true" : "false", + (events & DNS_POLLOUT) == DNS_POLLOUT ? "true" : "false"); + return; + } + + dns_rr_foreach(&rr, A, .sort = dns_rr_i_packet) { + if (rr.section == DNS_S_QD) { + /* don't parse repeated query */ + continue; + } + + if ((error = dns_any_parse(dns_any_init(&any, sizeof(any)), &rr, A)) != 0) { + OONF_WARN(LOG_DNS_QUERY, "Could not parse data of RR type %s (%d): %s (%d)", dns_strtype(rr.type), rr.type, dns_strerror(error), error); + continue; + } + + dns_any_print(buffer, sizeof(buffer), &any,rr.type); + OONF_DEBUG(LOG_DNS_QUERY, "Got RR (%s): %s", dns_strtype(rr.type), buffer); + + specific_callback = false; + switch (rr.type) { + case DNS_T_SRV: + if (q->cb.srv_result) { + specific_callback = true; + q->cb.srv_result(q, &any.srv); + } + break; + case DNS_T_A: + if (q->cb.a_result) { + specific_callback = true; + q->cb.a_result(q, &any.a); + } + break; + case DNS_T_AAAA: + if (q->cb.aaaa_result) { + specific_callback = true; + q->cb.aaaa_result(q, &any.aaaa); + } + break; + case DNS_T_PTR: + if (q->cb.ptr_result) { + specific_callback = true; + q->cb.ptr_result(q, &any.ptr); + } + break; + default: + break; + } + if (!specific_callback && q->cb.any_result) { + q->cb.any_result(q, rr.type, &any); + } + } + dns_so_close(q->_dns_socket); + oonf_socket_remove(&q->_socket_entry); + oonf_timer_stop(&q->_timeout); + + /* inform callback last (might cleanup data structure) */ + OONF_DEBUG(LOG_DNS_QUERY, "Query done"); + if (q->cb_done) { + q->cb_done(q, false); + } +} + +static void +_cb_dns_timeout(struct oonf_timer_instance *timer) { + struct oonf_dns_query *q; + + q = container_of(timer, struct oonf_dns_query, _timeout); + dns_so_close(q->_dns_socket); + oonf_socket_remove(&q->_socket_entry); + + /* inform callback last (might cleanup data structure */ + if (q->cb_done) { + q->cb_done(q, true); + } +} + +static void +_cb_config_changed(void) { + if (cfg_schema_tobin(&_config, _dns_query_section.post, _dns_query_entries, ARRAYSIZE(_dns_query_entries))) { + OONF_WARN(LOG_DNS_QUERY, "Could not convert " OONF_DNS_QUERY_SUBSYSTEM " config to bin"); + return; + } +} diff --git a/src/generic/dns_sd/CMakeLists.txt b/src/generic/dns_sd/CMakeLists.txt new file mode 100644 index 00000000..e113d951 --- /dev/null +++ b/src/generic/dns_sd/CMakeLists.txt @@ -0,0 +1,5 @@ +# set library parameters +SET (name dns_sd) + +# use generic plugin maker +oonf_create_plugin("${name}" "${name}.c" "${name}.h" "") diff --git a/src/generic/dns_sd/dns_sd.c b/src/generic/dns_sd/dns_sd.c new file mode 100644 index 00000000..633588a5 --- /dev/null +++ b/src/generic/dns_sd/dns_sd.c @@ -0,0 +1,889 @@ +/** + * Copyright (C) 2016 Fraunhofer FKIE + * + * @author Henning Rogge + */ + +/** + * @file + */ + +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include + +#define LOG_DNS_SD _dns_sd_subsystem.logging + +struct _dns_sd_config { + struct strarray prefix; +}; + +enum sndsd_cfg { + CFG_PREFIX +}; + +#define KEY_CONTEXT_IF "ctx_if" +#define KEY_CONTEXT_IP "ctx_ip" +#define KEY_CONTEXT_HOST "ctx_host" +#define KEY_SERVICE_HOST "service_host" +#define KEY_SERVICE_PREFIX "service_prefix" +#define KEY_SERVICE_PORT "service_port" +#define KEY_SERVICE_PRIO "service_priority" +#define KEY_SERVICE_WEIGHT "service_weight" +#define KEY_SERVICE_IPV4 "service_ipv4" +#define KEY_SERVICE_IPV6 "service_ipv6" + +static int _init(void); +static void _cleanup(void); + +static struct dns_sd_context *_add_sd_context(const char *ifname, const struct netaddr *ip); +static void _remove_sd_context(struct dns_sd_context *context); +static struct dns_sd_service *_add_sd_service( + struct dns_sd_context *context, struct dns_sd_prefix *prefix, const char *hostname); +static void _remove_sd_service(struct dns_sd_context *, struct dns_sd_service *service); + +static void _start_next_query(void); +static void _cb_a_result(struct oonf_dns_query *, struct dns_a *response); +static void _cb_aaaa_result(struct oonf_dns_query *, struct dns_aaaa *response); +static void _cb_srv_result(struct oonf_dns_query *, struct dns_srv *response); +static void _cb_ptr_result(struct oonf_dns_query *, struct dns_ptr *response); +static void _cb_query_done(struct oonf_dns_query *, bool timeout); + +static enum oonf_telnet_result _cb_dnssd_cmd(struct oonf_telnet_data *con); +static enum oonf_telnet_result _cb_dnssd_help(struct oonf_telnet_data *con); +static int _cb_create_text_context(struct oonf_viewer_template *); +static int _cb_create_text_service(struct oonf_viewer_template *); +static int _cb_create_text_prefix(struct oonf_viewer_template *); +static void _initialize_context_values(struct dns_sd_context *); +static void _initialize_service_values(struct dns_sd_service *); + +static void _cb_l2neighip_added(void *); +static int _avl_comp_sd_context(const void *k1, const void *k2); +static int _avl_comp_sd_service(const void *k1, const void *k2); +static void _cb_config_changed(void); + +/* telnet interface */ +static char _value_ctx_if[IF_NAMESIZE]; +static struct netaddr_str _value_ctx_ip; +static char _value_ctx_host[512]; +static char _value_service_host[512]; +static char _value_service_prefix[DNS_SD_PREFIX_LENGTH]; +static char _value_service_port[6]; +static char _value_service_priority[6]; +static char _value_service_weight[6]; +static struct netaddr_str _value_service_ipv4; +static struct netaddr_str _value_service_ipv6; + +static struct abuf_template_data_entry _tde_context_key[] = { + { KEY_CONTEXT_IF, _value_ctx_if, true }, + { KEY_CONTEXT_IP, _value_ctx_ip.buf, true }, +}; +static struct abuf_template_data_entry _tde_context[] = { + { KEY_CONTEXT_HOST, _value_ctx_host, true }, +}; +static struct abuf_template_data_entry _tde_service_key[] = { + { KEY_SERVICE_HOST, _value_service_host, true }, + { KEY_SERVICE_PREFIX, _value_service_prefix, true }, +}; +static struct abuf_template_data_entry _tde_service[] = { + { KEY_SERVICE_PORT, _value_service_port, false }, + { KEY_SERVICE_PRIO, _value_service_priority, false }, + { KEY_SERVICE_WEIGHT, _value_service_weight, false }, + { KEY_SERVICE_IPV4, _value_service_ipv4.buf, true }, + { KEY_SERVICE_IPV6, _value_service_ipv6.buf, true }, +}; +static struct abuf_template_data_entry _tde_prefix_key[] = { + { KEY_SERVICE_PREFIX, _value_service_prefix, true }, +}; + +static struct abuf_template_data _td_context[] = { + { _tde_context_key, ARRAYSIZE(_tde_context_key) }, + { _tde_context, ARRAYSIZE(_tde_context) }, +}; +static struct abuf_template_data _td_service[] = { + { _tde_context_key, ARRAYSIZE(_tde_context_key) }, + { _tde_context, ARRAYSIZE(_tde_context) }, + { _tde_service_key, ARRAYSIZE(_tde_service_key) }, + { _tde_service, ARRAYSIZE(_tde_service) }, +}; +static struct abuf_template_data _td_prefix[] = { + { _tde_prefix_key, ARRAYSIZE(_tde_prefix_key) }, +}; + +static struct oonf_viewer_template _templates[] = { + { + .data = _td_context, + .data_size = ARRAYSIZE(_td_context), + .json_name = "context", + .cb_function = _cb_create_text_context, + }, + { + .data = _td_service, + .data_size = ARRAYSIZE(_td_service), + .json_name = "service", + .cb_function = _cb_create_text_service, + }, + { + .data = _td_prefix, + .data_size = ARRAYSIZE(_td_prefix), + .json_name = "prefix", + .cb_function = _cb_create_text_prefix, + }, +}; + +static struct abuf_template_storage _template_storage; + +static struct oonf_telnet_command _dnssd_cmd = + TELNET_CMD("dnssd", _cb_dnssd_cmd, "", .help_handler = _cb_dnssd_help); + +/* configuration */ +static struct cfg_schema_entry _dns_sd_entries[] = { + [CFG_PREFIX] = CFG_MAP_STRINGLIST(_dns_sd_config, prefix, "prefix", "", "Prefix for DNS service lookup"), +}; + +static struct cfg_schema_section _dns_sd_section = { + .type = OONF_DNS_SD_SUBSYSTEM, + .cb_delta_handler = _cb_config_changed, + .entries = _dns_sd_entries, + .entry_count = ARRAYSIZE(_dns_sd_entries), +}; + +static struct _dns_sd_config _config; + +/* plugin declaration */ +static const char *_dependencies[] = { + OONF_CLASS_SUBSYSTEM, + OONF_LAYER2_SUBSYSTEM, + OONF_OS_INTERFACE_SUBSYSTEM, + OONF_DNS_QUERY_SUBSYSTEM, + OONF_TELNET_SUBSYSTEM, + OONF_VIEWER_SUBSYSTEM, +}; +static struct oonf_subsystem _dns_sd_subsystem = { + .name = OONF_DNS_SD_SUBSYSTEM, + .dependencies = _dependencies, + .dependencies_count = ARRAYSIZE(_dependencies), + .descr = "OONF dns service-discovery plugin", + .author = "Henning Rogge", + + .cfg_section = &_dns_sd_section, + + .init = _init, + .cleanup = _cleanup, +}; +DECLARE_OONF_PLUGIN(_dns_sd_subsystem); + +/* storage for sd prefixes */ +static struct oonf_class _sd_prefix_class = { + .name = "sd prefix", + .size = sizeof(struct dns_sd_prefix), +}; + +static struct oonf_class _sd_context_class = { + .name = "sd context", + .size = sizeof(struct dns_sd_context), +}; + +static struct oonf_class _sd_service_class = { + .name = "sd result", + .size = sizeof(struct dns_sd_service), +}; + +static struct avl_tree _prefix_tree; +static struct avl_tree _context_tree; +static uint64_t _used_flags = 0; + +/* class extension for layer2 neighbor ips */ +struct oonf_class_extension _l2neighip_ext = { + .ext_name = "dns sd", + .class_name = LAYER2_CLASS_NEIGHBOR_ADDRESS, + + .cb_add = _cb_l2neighip_added, +}; + +/* DNS query in progress */ +static struct dns_sd_query _dns; + +/* list of l2 entities that want a DNS update */ +static struct list_entity _update_list; + +/* tree of all dns-sd contexts */ +static struct avl_tree _context_tree; + +static int +_init(void) { + oonf_class_extension_add(&_l2neighip_ext); + oonf_class_add(&_sd_prefix_class); + oonf_class_add(&_sd_context_class); + oonf_class_add(&_sd_service_class); + + oonf_telnet_add(&_dnssd_cmd); + avl_init(&_prefix_tree, avl_comp_strcasecmp, false); + avl_init(&_context_tree, _avl_comp_sd_context, false); + list_init_head(&_update_list); + + memset(&_dns, 0, sizeof(_dns)); + _dns.dns.cb.a_result = _cb_a_result; + _dns.dns.cb.aaaa_result = _cb_aaaa_result; + _dns.dns.cb.ptr_result = _cb_ptr_result; + _dns.dns.cb.srv_result = _cb_srv_result; + _dns.dns.cb_done = _cb_query_done; + _dns.dns.query = _dns.name; + _dns.dns.dns_client = &_dns.client; + _dns.dns.dns_server = &_dns.server; + return 0; +} + +static void +_cleanup(void) { + struct dns_sd_prefix *prefix, *p_it; + + avl_for_each_element_safe(&_prefix_tree, prefix, _node, p_it) { + dns_sd_remove(prefix); + } + + oonf_telnet_remove(&_dnssd_cmd); + oonf_class_extension_remove(&_l2neighip_ext); + oonf_class_remove(&_sd_prefix_class); + oonf_class_remove(&_sd_context_class); + oonf_class_remove(&_sd_service_class); +} + +struct dns_sd_prefix * +dns_sd_add(const char *name) { + struct dns_sd_prefix *prefix; + uint64_t i; + + prefix = avl_find_element(&_prefix_tree, name, prefix, _node); + if (!prefix) { + if (_used_flags == ~0ull) { + OONF_WARN(LOG_DNS_SD, "Maximum number of active service discovery strings reached"); + return NULL; + } + + prefix = oonf_class_malloc(&_sd_prefix_class); + if (!prefix) { + return NULL; + } + + /* add to tree */ + strscpy(prefix->dns_prefix, name, sizeof(prefix->dns_prefix)); + prefix->_node.key = prefix->dns_prefix; + avl_insert(&_prefix_tree, &prefix->_node); + + /* acquire new flag */ + for (i=0; i<63; i++) { + if ((_used_flags & (1ull<_flag = 1ull<_flag; + break; + } + } + OONF_ASSERT(prefix->_flag, LOG_DNS_SD, "Could not aquire a DNS_SD flag"); + } + + /* increase usage counter */ + prefix->_usage++; + + return prefix; +} + +void +dns_sd_remove(struct dns_sd_prefix *prefix) { + struct dns_sd_context *context, *c_it; + struct dns_sd_service *service, *s_it; + + if (prefix->_usage > 1) { + prefix->_usage--; + return; + } + + avl_for_each_element_safe(&_context_tree, context, _global_node, c_it) { + if (dns_sd_context_has_prefix(prefix, context) == DNS_SD_PREFIX_AVAILABLE) { + context->available &= ~prefix->_flag; + context->unavailable &= ~prefix->_flag; + + avl_for_each_element_safe(&context->_service_tree, service, _node, s_it) { + if (service->key.prefix == prefix) { + _remove_sd_service(context, service); + } + } + } + } + + _used_flags &= ~(prefix->_flag); + avl_remove(&_prefix_tree, &prefix->_node); + oonf_class_free(&_sd_prefix_class, prefix); +} + +struct dns_sd_context * +dns_sd_context_get(const char *interface, const struct netaddr *ip) { + struct dns_sd_context *context; + struct dns_sd_context_key key; + + memset(&key, 0, sizeof(key)); + strscpy(key.interface, interface, sizeof(key.interface)); + memcpy(&key.ip, ip, sizeof(key.ip)); + return avl_find_element(dns_sd_get_context_tree(), &key, context, _global_node); +} + +struct avl_tree * +dns_sd_get_prefix_tree(void) { + return &_prefix_tree; +} + +struct avl_tree * +dns_sd_get_context_tree(void) { + return &_context_tree; +} + +static struct dns_sd_context * +_add_sd_context(const char *ifname, const struct netaddr *ip) { + struct dns_sd_context *context; + struct dns_sd_context_key key; + + memset(&key, 0, sizeof(key)); + strscpy(key.interface, ifname, sizeof(key.interface)); + memcpy(&key.ip, ip, sizeof(key.ip)); + + context = avl_find_element(&_context_tree, &key, context, _global_node); + if (context) { + return context; + } + + context = oonf_class_malloc(&_sd_context_class); + if (!context) { + return NULL; + } + + memcpy(&context->key, &key, sizeof(key)); + + context->_global_node.key = &context->key; + avl_insert(&_context_tree, &context->_global_node); + + avl_init(&context->_service_tree, _avl_comp_sd_service, false); + return context; +} + +static void +_remove_sd_context(struct dns_sd_context *context) { + struct dns_sd_service *service, *s_it; + + avl_for_each_element_safe(&context->_service_tree, service, _node, s_it) { + _remove_sd_service(context, service); + } + + avl_remove(&_context_tree, &context->_global_node); + oonf_class_free(&_sd_context_class, context); +} + +static struct dns_sd_service * +_add_sd_service(struct dns_sd_context *context, + struct dns_sd_prefix *prefix, const char *hostname) { + struct dns_sd_service *service; + struct dns_sd_service_key key; + + memset(&key, 0, sizeof(key)); + key.hostname = hostname; + key.prefix = prefix; + + service = avl_find_element(&context->_service_tree, &key, service, _node); + if (service) { + return service; + } + + service = oonf_class_malloc(&_sd_service_class); + if (!service) { + return NULL; + } + + service->key.hostname = strdup(hostname); + service->key.prefix = prefix; + + service->_node.key = &service->key; + avl_insert(&context->_service_tree, &service->_node); + + return service; +} + +static void +_remove_sd_service(struct dns_sd_context *context, struct dns_sd_service *service) { + free ((char *)service->key.hostname); + avl_remove(&context->_service_tree, &service->_node); + oonf_class_free(&_sd_service_class, service); + + if (context->_service_tree.count == 0) { + _remove_sd_context(context); + } +} + +static int +_get_rdns_arpa_name(struct dns_sd_query *dns, struct netaddr *ip) { + static const char HEX[]="0123456789abcdef"; + const uint8_t *bin; + char *name; + int i; + + bin = netaddr_get_binptr(ip); + if (netaddr_get_address_family(ip) == AF_INET) { + // 4.4.8.8.in-addr.arpa + snprintf(dns->name, sizeof(dns->name), "%u.%u.%u.%u.in-addr.arpa", + bin[3], bin[2], bin[1], bin[0]); + return 0; + } + if (netaddr_get_address_family(ip) == AF_INET6) { + // b.a.9.8.7.6.5.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.b.d.0.1.0.0.2.ip6.arpa + name = dns->name; + for (i=15; i>=0; i--) { + *name++ = HEX[bin[i] >> 4]; + *name++ = '.'; + *name++ = HEX[bin[i] & 0x0f]; + *name++ = '.'; + } + strscpy(name, "ip6.arpa", 10); + return 0; + } + return -1; +} + +static int +_work_on_l2neigh_addr(struct dns_sd_query *dns) { + const struct os_interface_ip *if_ip; + struct dns_sd_prefix *sd_prefix; + struct os_interface *os_if; + struct netaddr dns_ip; + uint64_t done; + +#ifdef OONF_LOG_DEBUG_INFO + struct netaddr_str nbuf; +#endif + + OONF_DEBUG(LOG_DNS_SD, "Work on l2neigh_addr: %s", netaddr_to_string(&nbuf, &dns->context->key.ip)); + + if (!list_is_node_added(&dns->context->_working_node)) { + /* node is not in update-list anymore */ + return -1; + } + + /* set DNS client address */ + netaddr_from_socket(&dns_ip, &dns->server); + os_if = os_interface_get(dns->context->key.interface); + if (!os_if) { + OONF_WARN(LOG_DNS_SD, "No os itnerface data for '%s'", dns->context->key.interface); + list_remove(&dns->context->_working_node); + return -1; + } + if_ip = os_interface_get_prefix_from_dst(&dns_ip, os_if); + if (!if_ip) { + OONF_WARN(LOG_DNS_SD, "No fitting IP address for DNS server on interface %s", + dns->context->key.interface); + list_remove(&dns->context->_working_node); + return -1; + } + netaddr_socket_init(&dns->client, &if_ip->address, 0, + netaddr_socket_get_scopex(&dns->server)); + + if (!dns->context->hostname) { + /* get hostname */ + if (_get_rdns_arpa_name(&_dns, &dns->context->key.ip)) { + list_remove(&dns->context->_working_node); + return -1; + } + + dns->prefix = NULL; + if (dns_query_ptr(&_dns.dns)) { + list_remove(&dns->context->_working_node); + return -1; + } + return 0; + } + + /* search for first unknown service entry */ + done = dns->context->available | dns->context->unavailable; + avl_for_each_element(&_prefix_tree, sd_prefix, _node) { + if ((done & sd_prefix->_flag) == 0) { + strscpy(dns->name, sd_prefix->dns_prefix, sizeof(dns->name)); + strscat(dns->name, dns->context->hostname, sizeof(dns->name)); + + dns->prefix = sd_prefix; + memset(&dns->srv_result, 0, sizeof(dns->srv_result)); + if (dns_query_srv(&_dns.dns)) { + list_remove(&dns->context->_working_node); + return -1; + } + return 0; + } + } + list_remove(&dns->context->_working_node); + return -1; +} + +static void +_start_next_query(void) { + struct dns_sd_context *context; + struct oonf_layer2_net *l2net; + const union netaddr_socket *sock_ptr; + + OONF_DEBUG(LOG_DNS_SD, "start next query"); + while (!list_is_empty(&_update_list)) { + OONF_DEBUG(LOG_DNS_SD, "loop"); + context = list_first_element(&_update_list, context, _working_node); + + /* get DNS server socket */ + l2net = oonf_layer2_net_get(context->key.interface); + if (!l2net) { + /* no external DNS server */ + OONF_WARN(LOG_DNS_SD, "No DNS server available for l2 interface '%s'", l2net->name); + list_remove(&context->_working_node); + continue; + } + + sock_ptr = oonf_layer2_data_get_socket(&l2net->data[OONF_LAYER2_NET_IPV6_REMOTE_DNS]); + if (!sock_ptr || netaddr_socket_is_unspec(sock_ptr)) { + sock_ptr = oonf_layer2_data_get_socket(&l2net->data[OONF_LAYER2_NET_IPV4_REMOTE_DNS]); + if (!sock_ptr || netaddr_socket_is_unspec(sock_ptr)) { + /* no external DNS server */ + OONF_WARN(LOG_DNS_SD, "No DNS server available for l2 interface '%s'", l2net->name); + + list_remove(&context->_working_node); + continue; + } + memcpy(&_dns.server, sock_ptr, sizeof(_dns.server)); + } + else { + /* set interface index for IPv6 */ + memcpy(&_dns.server, sock_ptr, sizeof(_dns.server)); + _dns.server.v6.sin6_scope_id = l2net->if_listener.data->index; + } + + _dns.context = context; + if (!_work_on_l2neigh_addr(&_dns)) { + OONF_DEBUG(LOG_DNS_SD, "Query started"); + return; + } + /* query in progress */ + OONF_DEBUG(LOG_DNS_SD, "Work failed"); + } +} + +static void +_enqueue_dns_query(const char *ifname, const struct netaddr *ip) { + struct dns_sd_context *context; + bool in_progress; + + context = _add_sd_context(ifname, ip); + if (!context) { + return; + } + + if (list_is_node_added(&context->_working_node)) { + /* node is already in update list */ + return; + } + + /* update list is not empty means a query is ongoing */ + in_progress = !list_is_empty(&_update_list); + + /* put new address into waiting list */ + list_add_tail(&_update_list, &context->_working_node); + if (in_progress) { + return; + } + _start_next_query(); +} + +static void +_cb_a_result(struct oonf_dns_query *q, struct dns_a *response) { + struct dns_sd_query *dnssd_q; + struct netaddr *addr; + + if (q->dns_type != DNS_T_SRV) { + return; + } + + dnssd_q = container_of(q, struct dns_sd_query, dns); + if (dnssd_q->srv_result.service) { + addr = &dnssd_q->srv_result.service->ipv4; + } + else { + addr = &dnssd_q->srv_result.ipv4; + } + netaddr_from_binary(addr, &response->addr, 4, AF_INET); +} + +static void +_cb_aaaa_result(struct oonf_dns_query *q, struct dns_aaaa *response) { + struct dns_sd_query *dnssd_q; + struct netaddr *addr; + + if (q->dns_type != DNS_T_SRV) { + return; + } + + dnssd_q = container_of(q, struct dns_sd_query, dns); + if (dnssd_q->srv_result.service) { + addr = &dnssd_q->srv_result.service->ipv6; + } + else { + addr = &dnssd_q->srv_result.ipv6; + } + netaddr_from_binary(addr, &response->addr, 16, AF_INET6); +} + +static void +_cb_srv_result(struct oonf_dns_query *q, struct dns_srv *response) { + struct dns_sd_service *service; + struct dns_sd_query *dnssd_q; + + dnssd_q = container_of(q, struct dns_sd_query, dns); + if (dnssd_q->context != NULL && dnssd_q->prefix != NULL) { + /* neighbor and prefix are still available */ + dnssd_q->context->available |= dnssd_q->prefix->_flag; + + service = _add_sd_service(dnssd_q->context, dnssd_q->prefix, response->target); + if (service) { + dnssd_q->srv_result.service = service; + + service->port = response->port; + service->weight = response->weight; + service->priority = response->priority; + + if (!netaddr_is_unspec(&dnssd_q->srv_result.ipv4)) { + memcpy(&service->ipv4, &dnssd_q->srv_result.ipv4, sizeof(service->ipv4)); + } + if (!netaddr_is_unspec(&dnssd_q->srv_result.ipv6)) { + memcpy(&service->ipv6, &dnssd_q->srv_result.ipv6, sizeof(service->ipv6)); + } + } + } +} + +static void +_cb_ptr_result(struct oonf_dns_query *q, struct dns_ptr *response) { + struct dns_sd_query *dnssd_q; + + dnssd_q = container_of(q, struct dns_sd_query, dns); + if (dnssd_q->context) { + /* neighbor is still available */ + if (!dnssd_q->context->hostname) { + dnssd_q->context->hostname = strdup(response->host); + } + } +} + +static void +_cb_query_done(struct oonf_dns_query *q, bool timeout) { + struct dns_sd_query *dnssd_q; + struct dns_sd_service *service; +#ifdef OONF_LOG_INFO + struct netaddr_str nbuf1, nbuf2; +#endif + + OONF_DEBUG(LOG_DNS_SD, "query done callback"); + + dnssd_q = container_of(q, struct dns_sd_query, dns); + if (!dnssd_q->context) { + // trigger next query */ + _start_next_query(); + return; + } + if (dnssd_q->dns.dns_type == DNS_T_SRV && !dnssd_q->prefix) { + // trigger next query */ + list_remove(&dnssd_q->context->_working_node); + _start_next_query(); + return; + } + + service = dnssd_q->srv_result.service; + if (dnssd_q->dns.dns_type == DNS_T_SRV && service != NULL) { + if (!netaddr_is_unspec(&service->ipv4)) { + OONF_INFO(LOG_DNS_SD, "SRV result for %s: [%s]:%u w=%u p=%u", + netaddr_to_string(&nbuf1, &dnssd_q->context->key.ip), + netaddr_to_string(&nbuf2, &service->ipv4), + service->port, + service->weight, + service->priority); + } + if (!netaddr_is_unspec(&service->ipv6)) { + OONF_INFO(LOG_DNS_SD, "SRV result for %s: [%s]:%u w=%u p=%u", + netaddr_to_string(&nbuf1, &dnssd_q->context->key.ip), + netaddr_to_string(&nbuf2, &service->ipv6), + service->port, + service->weight, + service->priority); + } + } + if (timeout) { + list_remove(&dnssd_q->context->_working_node); + } + else if (dns_query_get_type(q) == DNS_T_PTR) { + /* host query is done */ + if (!dnssd_q->context->hostname) { + list_remove(&dnssd_q->context->_working_node); + } + } + else if (dns_query_get_type(q) == DNS_T_SRV) { + /* got service record */ + dnssd_q->context->unavailable |= dnssd_q->prefix->_flag; + } + + /* trigger next query */ + if (_work_on_l2neigh_addr(&_dns)) { + _start_next_query(); + } +} + +static enum oonf_telnet_result +_cb_dnssd_cmd(struct oonf_telnet_data *con) { + return oonf_viewer_telnet_handler( + con->out, &_template_storage, OONF_DNS_SD_SUBSYSTEM, con->parameter, _templates, ARRAYSIZE(_templates)); +} + +static enum oonf_telnet_result +_cb_dnssd_help(struct oonf_telnet_data *con) { + return oonf_viewer_telnet_help( + con->out, OONF_DNS_SD_SUBSYSTEM, con->parameter, _templates, ARRAYSIZE(_templates)); +} + +static int +_cb_create_text_context(struct oonf_viewer_template *template) { + struct dns_sd_context *context; + + avl_for_each_element(&_context_tree, context, _global_node) { + _initialize_context_values(context); + oonf_viewer_output_print_line(template); + } + return 0; +} + +static int +_cb_create_text_service(struct oonf_viewer_template *template) { + struct dns_sd_context *context; + struct dns_sd_service *service; + + avl_for_each_element(&_context_tree, context, _global_node) { + _initialize_context_values(context); + + avl_for_each_element(&context->_service_tree, service, _node) { + _initialize_service_values(service); + oonf_viewer_output_print_line(template); + } + } + return 0; +} + +static int +_cb_create_text_prefix(struct oonf_viewer_template *template) { + struct dns_sd_prefix *prefix; + + avl_for_each_element(&_prefix_tree, prefix, _node) { + strscpy(_value_service_prefix, prefix->dns_prefix, sizeof(_value_service_prefix)); + oonf_viewer_output_print_line(template); + } + return 0; +} + +static void +_initialize_context_values(struct dns_sd_context *context) { + strscpy(_value_ctx_if, context->key.interface, IF_NAMESIZE); + netaddr_to_string(&_value_ctx_ip, &context->key.ip); + if (context->hostname) { + strscpy(_value_ctx_host, context->hostname, sizeof(_value_ctx_host)); + } + else { + _value_ctx_host[0] = 0; + } +} + +static void +_initialize_service_values(struct dns_sd_service *service) { + strscpy(_value_service_host, service->key.hostname, sizeof(_value_service_host)); + strscpy(_value_service_prefix, service->key.prefix->dns_prefix, sizeof(_value_service_prefix)); + snprintf(_value_service_port, sizeof(_value_service_port), "%u", service->port); + snprintf(_value_service_priority, sizeof(_value_service_priority), "%u", service->priority); + snprintf(_value_service_weight, sizeof(_value_service_weight), "%u", service->weight); + netaddr_to_string(&_value_service_ipv4, &service->ipv4); + netaddr_to_string(&_value_service_ipv6, &service->ipv6); +} + +static void +_cb_l2neighip_added(void *ptr) { + struct oonf_layer2_neighbor_address *l2neigh_addr = ptr; + + switch (netaddr_get_address_family(&l2neigh_addr->ip)) { + case AF_INET: + case AF_INET6: + if (netaddr_is_host(&l2neigh_addr->ip)) { + _enqueue_dns_query(l2neigh_addr->l2neigh->network->name, &l2neigh_addr->ip); + } + break; + default: + break; + } +} + +static int +_avl_comp_sd_context(const void *k1, const void *k2) { + const struct dns_sd_context_key *ck1 = k1; + const struct dns_sd_context_key *ck2 = k2; + + return memcmp(ck1, ck2, sizeof(*ck1)); +} + +static int +_avl_comp_sd_service(const void *k1, const void *k2) { + const struct dns_sd_service_key *sk1 = k1; + const struct dns_sd_service_key *sk2 = k2; + int diff; + + diff = strcmp(sk1->hostname, sk2->hostname); + if (diff) { + return diff; + } + return strcmp(sk1->prefix->dns_prefix, sk2->prefix->dns_prefix); +} + +static void +_cb_config_changed(void) { + struct dns_sd_prefix *sd_prefix; + struct cfg_entry *entry; + const char *ptr; + + if (cfg_schema_tobin(&_config, _dns_sd_section.post, _dns_sd_entries, ARRAYSIZE(_dns_sd_entries))) { + OONF_WARN(LOG_DNS_SD, "Could not convert " OONF_DNS_SD_SUBSYSTEM " config to bin"); + return; + } + + /* and new (and existing) prefixes */ + strarray_for_each_element(&_config.prefix, ptr) { + dns_sd_add(ptr); + } + + if (_dns_sd_section.pre) { + entry = cfg_db_get_entry(_dns_sd_section.pre, _dns_sd_entries[CFG_PREFIX].key.entry); + if (entry) { + /* remove old (and existing) prefixes */ + strarray_for_each_element(&_config.prefix, ptr) { + sd_prefix = avl_find_element(&_prefix_tree, ptr, sd_prefix, _node); + OONF_ASSERT (sd_prefix != NULL, LOG_DNS_SD, "to be removed SD prefix was not there"); + dns_sd_remove(sd_prefix); + } + } + } +} diff --git a/src/generic/layer2_config/layer2_config.c b/src/generic/layer2_config/layer2_config.c index da391bd9..7ea9a20a 100644 --- a/src/generic/layer2_config/layer2_config.c +++ b/src/generic/layer2_config/layer2_config.c @@ -385,6 +385,7 @@ _cb_if_value_validator(struct autobuf *out, const char *section_name, const char struct l2_config_data l2_data; const struct oonf_layer2_metadata *meta; union oonf_layer2_value dst; + int error; if (cfg_tobin_tokens(&l2_data, value, entries, entry_count, NULL)) { return -1; @@ -392,11 +393,13 @@ _cb_if_value_validator(struct autobuf *out, const char *section_name, const char meta = oonf_layer2_net_metadata_get(l2_data.data_idx); - if (oonf_layer2_data_parse_string(&dst, meta, l2_data.txt_value)) { + if ((error = oonf_layer2_data_parse_string(&dst, meta, l2_data.txt_value))) { cfg_append_printable_line(out, - "Value '%s' for entry '%s' in section %s does not use the data" - " type %s for layer2 network key %s", - value, entry_name, section_name, oonf_layer2_data_get_type_string(meta), meta->key); + "Value '%s' for entry '%s' in section %s does not use the data" + " type '%s' for layer2 network key %s (%d)", + l2_data.txt_value, entry_name, section_name, oonf_layer2_data_get_type_string(meta), + meta->key, error); + return -1; } return 0; } @@ -441,6 +444,7 @@ _cb_neigh_value_validator(struct autobuf *out, const char *section_name, const c struct l2_config_data l2_data; const struct oonf_layer2_metadata *meta; union oonf_layer2_value dst; + int error; if (cfg_tobin_tokens(&l2_data, value, entries, entry_count, NULL)) { return -1; @@ -448,11 +452,13 @@ _cb_neigh_value_validator(struct autobuf *out, const char *section_name, const c meta = oonf_layer2_neigh_metadata_get(l2_data.data_idx); - if (oonf_layer2_data_parse_string(&dst, meta, l2_data.txt_value)) { + if ((error = oonf_layer2_data_parse_string(&dst, meta, l2_data.txt_value))) { cfg_append_printable_line(out, - "Value '%s' for entry '%s' in section %s does not use the data" - " type %s for layer2 neighbor key %s", - value, entry_name, section_name, oonf_layer2_data_get_type_string(meta), meta->key); + "Value '%s' for entry '%s' in section %s does not use the data" + " type '%s' for layer2 neighbor key %s (%d)", + l2_data.txt_value, entry_name, section_name, oonf_layer2_data_get_type_string(meta), + meta->key, error); + return -1; } return 0; } From 045a52493a9a07129550ccf68be419975c5e3656 Mon Sep 17 00:00:00 2001 From: Henning Rogge Date: Fri, 10 May 2019 13:27:53 +0200 Subject: [PATCH 18/63] Add doxygen comments --- include/oonf/base/oonf_layer2.h | 139 ++++++++++++++++-- include/oonf/base/oonf_rfc5444.h | 9 ++ include/oonf/generic/dns_query/dns_query.h | 76 +++++++++- include/oonf/generic/dns_sd/dns_sd.h | 60 +++++++- include/oonf/libcommon/netaddr.h | 10 +- src/generic/dns_query/dns_query.c | 28 +++- src/generic/dns_sd/dns_sd.c | 160 ++++++++++++++++++++- src/generic/layer2info/layer2info.c | 39 +++-- 8 files changed, 487 insertions(+), 34 deletions(-) diff --git a/include/oonf/base/oonf_layer2.h b/include/oonf/base/oonf_layer2.h index 7f1eac33..7b07c4cd 100644 --- a/include/oonf/base/oonf_layer2.h +++ b/include/oonf/base/oonf_layer2.h @@ -228,10 +228,20 @@ union oonf_layer2_value { union netaddr_socket socket; }; +/** + * Metadata flags for network and socket data type + */ enum oonf_layer2_network_flags { + /*! IPv4 prefix/socket is allowed */ OONF_LAYER2_IPV4_DATA = 1<<0, + + /*! IPv6 prefix/socket is allowed */ OONF_LAYER2_IPV6_DATA = 1<<1, + + /*! unspecified prefix/socket is allowed */ OONF_LAYER2_UNSPEC_DATA = 1<<2, + + /*! Only hosts are allowed, no prefixes */ OONF_LAYER2_HOST_ONLY_DATA = 1<<3, }; @@ -492,12 +502,6 @@ struct oonf_layer2_net { /*! absolute timestamp when network has been active last */ uint64_t last_seen; - /*! - * DNS delivered that can be used for this interface, e.g. - * for DNS service discovery - */ - union netaddr_socket dns; - /*! network wide layer 2 data */ struct oonf_layer2_data data[OONF_LAYER2_NET_COUNT]; @@ -755,6 +759,12 @@ oonf_layer2_net_get_remote_ip(const struct oonf_layer2_net *l2net, const struct return avl_find_element(&l2net->remote_neighbor_ips, addr, l2ip, _net_node); } +/** + * Add a layer 2 neighbor without supplying a link-id + * @param l2net layer-2 network object + * @param l2neigh layer2 address of neighbor + * @return layer2 neighbor address object, NULL if out of memory + */ static INLINE struct oonf_layer2_neigh * oonf_layer2_neigh_add(struct oonf_layer2_net *net, const struct netaddr *l2neigh) { struct oonf_layer2_neigh_key key; @@ -792,11 +802,23 @@ oonf_layer2_neigh_get_lid(const struct oonf_layer2_net *l2net, const struct oonf return avl_find_element(&l2net->neighbors, key, l2neigh, _node); } +/** + * Check if a specific element of a neighbor was modified + * @param neigh layer-2 neighbor object + * @param mod_mask a binary mask of elements that can be modified + * @return true if one of the elements was modified, false otherwise + */ static INLINE bool oonf_layer2_neigh_is_modified(const struct oonf_layer2_neigh *neigh, enum oonf_layer2_neigh_mods mod_mask) { return (neigh->modified & mod_mask) != 0; } +/** + * Get the next hop (stored in the neighbor) of a certain address type + * @param neigh layer-2 neighbor object + * @param af_type AF_INET or AF_INET6 + * @return next hop addres object, might be unspecified + */ static INLINE const struct netaddr * oonf_layer2_neigh_get_nexthop(const struct oonf_layer2_neigh *neigh, int af_type) { switch (af_type) { @@ -809,6 +831,12 @@ oonf_layer2_neigh_get_nexthop(const struct oonf_layer2_neigh *neigh, int af_type } } +/** + * Check if neighbor has a next hop of a certain address type + * @param neigh layer-2 neighbor object + * @param af_type AF_INET or AF_INET6 + * @return true if next hop exists, false otherwise + */ static INLINE bool oonf_layer2_neigh_has_nexthop(const struct oonf_layer2_neigh *neigh, int af_type) { const struct netaddr *next_hop; @@ -817,11 +845,20 @@ oonf_layer2_neigh_has_nexthop(const struct oonf_layer2_neigh *neigh, int af_type return next_hop != NULL && netaddr_get_address_family(next_hop) == af_type; } +/** + * @param neigh layer-2 neighbor object + * @return time when the neighbor has been seen last + */ static INLINE uint64_t oonf_layer2_neigh_get_lastseen(const struct oonf_layer2_neigh *neigh) { return neigh->_last_seen; } +/** + * Sets when a neighbor has been seen last + * @param neigh layer-2 neighbor object + * @param lastseen time when the neighbor has been seen last + */ static INLINE void oonf_layer2_neigh_set_lastseen(struct oonf_layer2_neigh *neigh, uint64_t lastseen) { if (neigh->_last_seen != lastseen) { @@ -924,6 +961,11 @@ oonf_layer2_data_read_boolean(bool *buffer, const struct oonf_layer2_data *l2dat return 0; } +/** + * @param l2data layer-2 data object + * @return true if the data object contains no data, not a network data + * or network data which is unspecified, false otherwise + */ static INLINE bool oonf_layer2_data_netaddr_is_unspec(const struct oonf_layer2_data *l2data) { return !l2data->_meta @@ -943,6 +985,11 @@ oonf_layer2_data_get_netaddr(const struct oonf_layer2_data *l2data) { return &l2data->_value.addr; } +/** + * @param l2data layer-2 data object + * @return true if the data object contains no data, not socket data + * or socket data which is unspecified, false otherwise + */ static INLINE bool oonf_layer2_data_socket_is_unspec(const struct oonf_layer2_data *l2data) { return !l2data->_meta @@ -962,6 +1009,14 @@ oonf_layer2_data_get_socket(const struct oonf_layer2_data *l2data) { return &l2data->_value.socket; } +/** + * Sets a layer-2 object with network data + * @param l2data layer-2 data object + * @param origin layer-2 origin + * @param meta layer-2 metadata + * @param addr network data object + * @return true if the data could be set, false otherwise + */ static INLINE bool oonf_layer2_data_set_netaddr(struct oonf_layer2_data *l2data, const struct oonf_layer2_origin *origin, const struct oonf_layer2_metadata *meta, const struct netaddr *addr) { @@ -972,6 +1027,14 @@ oonf_layer2_data_set_netaddr(struct oonf_layer2_data *l2data, const struct oonf_ return oonf_layer2_data_set(l2data, origin, meta, value); } +/** + * Sets a layer-2 object with socket data + * @param l2data layer-2 data object + * @param origin layer-2 origin + * @param meta layer-2 metadata + * @param sock socket data + * @return true if the data could be set, false otherwise + */ static INLINE bool oonf_layer2_data_set_socket(struct oonf_layer2_data *l2data, const struct oonf_layer2_origin *origin, const struct oonf_layer2_metadata *meta, const union netaddr_socket *sock) { @@ -1014,6 +1077,15 @@ oonf_layer2_data_set_origin(struct oonf_layer2_data *l2data, const struct oonf_l l2data->_origin = origin; } + +/** + * Convert a text input into a layer-2 data object + * @param data destination buffer + * @param origin layer-2 origin of data + * @param meta layer-2 metadata object + * @param input text input + * @return true if conversion was successful, false otherwise + */ static INLINE bool oonf_layer2_data_from_string(struct oonf_layer2_data *data, const struct oonf_layer2_origin *origin, const struct oonf_layer2_metadata *meta, const char *input) { @@ -1025,16 +1097,47 @@ oonf_layer2_data_from_string(struct oonf_layer2_data *data, const struct oonf_la return oonf_layer2_data_set(data, origin, meta, &value); } +/** + * Converts a network data object into a string + * @param buffer destination string buffer + * @param length length of destination buffer + * @param l2net layer-2 network object + * @param idx network data object index + * @param raw true for raw conversion, false for iso-prefix conversion + */ static INLINE const char * oonf_layer2_net_data_to_string( - char *buffer, size_t length, const struct oonf_layer2_data *data, enum oonf_layer2_network_index idx, bool raw) { - return oonf_layer2_data_to_string(buffer, length, data, oonf_layer2_net_metadata_get(idx), raw); + char *buffer, size_t length, struct oonf_layer2_net *l2net, enum oonf_layer2_network_index idx, bool raw) { + return oonf_layer2_data_to_string(buffer, length, &l2net->data[idx], oonf_layer2_net_metadata_get(idx), raw); +} + +/** + * Converts a network specific neighbor default data object into a string + * @param buffer destination string buffer + * @param length length of destination buffer + * @param l2net layer-2 network object + * @param idx neighbor data object index + * @param raw true for raw conversion, false for iso-prefix conversion + */ +static INLINE const char * +oonf_layer2_net_default_data_to_string( + char *buffer, size_t length, struct oonf_layer2_net *l2net, enum oonf_layer2_network_index idx, bool raw) { + return oonf_layer2_data_to_string(buffer, length, &l2net->data[idx], oonf_layer2_net_metadata_get(idx), raw); } + +/** + * Converts a neighbor data object into a string + * @param buffer destination string buffer + * @param length length of destination buffer + * @param l2neigh layer-2 neighbor object + * @param idx neighbor data object index + * @param raw true for raw conversion, false for iso-prefix conversion + */ static INLINE const char * oonf_layer2_neigh_data_to_string( - char *buffer, size_t length, const struct oonf_layer2_data *data, enum oonf_layer2_neighbor_index idx, bool raw) { - return oonf_layer2_data_to_string(buffer, length, data, oonf_layer2_neigh_metadata_get(idx), raw); + char *buffer, size_t length, struct oonf_layer2_neigh *l2neigh, enum oonf_layer2_neighbor_index idx, bool raw) { + return oonf_layer2_data_to_string(buffer, length, &l2neigh->data[idx], oonf_layer2_neigh_metadata_get(idx), raw); } /** @@ -1053,12 +1156,28 @@ oonf_layer2_data_set_bool(struct oonf_layer2_data *l2data, const struct oonf_lay return oonf_layer2_data_set(l2data, origin, meta, &value); } +/** + * Parse a string into a network data base layer-2 data object + * @param data destination buffer + * @param idx network data index + * @param origin layer-2 data origin + * @param input input string buffer + * @return 0 if everything is okay, -1 otherwise + */ static INLINE int oonf_layer2_net_data_from_string(struct oonf_layer2_data *data, enum oonf_layer2_network_index idx, struct oonf_layer2_origin *origin, const char *input) { return oonf_layer2_data_from_string(data, origin, oonf_layer2_net_metadata_get(idx), input); } +/** + * Parse a string into a neighbor data base layer-2 data object + * @param data destination buffer + * @param idx neighbor data index + * @param origin layer-2 data origin + * @param input input string buffer + * @return 0 if everything is okay, -1 otherwise + */ static INLINE int oonf_layer2_neigh_data_from_string(struct oonf_layer2_data *data, enum oonf_layer2_neighbor_index idx, struct oonf_layer2_origin *origin, const char *input) { diff --git a/include/oonf/base/oonf_rfc5444.h b/include/oonf/base/oonf_rfc5444.h index e3e75d6c..5cf13cbf 100644 --- a/include/oonf/base/oonf_rfc5444.h +++ b/include/oonf/base/oonf_rfc5444.h @@ -359,11 +359,20 @@ oonf_rfc5444_is_target_active(struct oonf_rfc5444_target *target) { oonf_packet_managed_is_active(&target->interface->_socket, netaddr_get_address_family(&target->dst)); } +/** + * @param pointer to rfc5444 interface + * @returns true if interface is in routing mode, false otherwise + */ static INLINE bool oonf_rfc5444_is_interface_routing(struct oonf_rfc5444_interface *interface) { return !interface->_socket.config.dont_route; } +/** + * Sets the routing mode of a rfc5444 interface + * @param pointer to rfc5444 interface + * @param route true to acticate routing mode, false to deactivate it + */ static INLINE void oonf_rfc5444_set_interface_routing(struct oonf_rfc5444_interface *interface, bool route) { if (oonf_rfc5444_is_interface_routing(interface) != route) { diff --git a/include/oonf/generic/dns_query/dns_query.h b/include/oonf/generic/dns_query/dns_query.h index 822bb476..f3f88982 100644 --- a/include/oonf/generic/dns_query/dns_query.h +++ b/include/oonf/generic/dns_query/dns_query.h @@ -1,7 +1,42 @@ -/** - * Copyright (C) 2019 Fraunhofer FKIE + +/* + * The olsr.org Optimized Link-State Routing daemon version 2 (olsrd2) + * Copyright (c) 2004-2019, the olsr.org team - see HISTORY file + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of olsr.org, olsrd nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * Visit http://www.olsr.org for more information. + * + * If you find this software useful feel free to make a donation + * to the project. For more information see the website or contact + * the copyright holders. * - * @author Henning Rogge */ /** @@ -39,16 +74,19 @@ struct oonf_dns_query { /*! DNS client socket */ union netaddr_socket *dns_client; + /*! type of DNS query (e.g. A, AAAA, PTR) */ enum dns_type dns_type; + /*! query */ const char *query; + /*! name of socket to register with scheduler */ const char *socket_name; - /* function to call when done */ + /*! function to call when done */ void (*cb_done)(struct oonf_dns_query *, bool timeout); - /* function to call with result */ + /*! function to call with result */ struct { void (*srv_result)(struct oonf_dns_query *, struct dns_srv *response); void (*a_result)(struct oonf_dns_query *, struct dns_a *response); @@ -57,44 +95,72 @@ struct oonf_dns_query { void (*any_result)(struct oonf_dns_query *, enum dns_type type, union dns_any *response); } cb; + /*! time until the query will be stopped */ uint64_t timeout; + /*! storage for binary query */ union { struct dns_packet packet; uint8_t full_length[dns_p_calcsize((512))]; } _bin_query; + /*! pointer to store DNS socket */ struct dns_socket *_dns_socket; + + /*! scheduler entry for DNS socket */ struct oonf_socket_entry _socket_entry; + + /*! timeout instance for query */ struct oonf_timer_instance _timeout; }; int dns_query_do(struct oonf_dns_query *q); +/** + * Start a DNS SRV query + * @param q intialized query + */ static INLINE int dns_query_srv(struct oonf_dns_query *q) { q->dns_type = DNS_T_SRV; return dns_query_do(q); } +/** + * Start a DNS A query + * @param q intialized query + */ static INLINE int dns_query_a(struct oonf_dns_query *q) { q->dns_type = DNS_T_A; return dns_query_do(q); } +/** + * Start a DNS AAAA query + * @param q intialized query + */ static INLINE int dns_query_aaaa(struct oonf_dns_query *q) { q->dns_type = DNS_T_AAAA; return dns_query_do(q); } +/** + * Start a DNS PTR query + * @param q intialized query + */ static INLINE int dns_query_ptr(struct oonf_dns_query *q) { q->dns_type = DNS_T_PTR; return dns_query_do(q); } +/** + * Get type of DNS query + * @param q DNS query + * @returns type + */ static INLINE enum dns_type dns_query_get_type(struct oonf_dns_query *q) { return q->dns_type; diff --git a/include/oonf/generic/dns_sd/dns_sd.h b/include/oonf/generic/dns_sd/dns_sd.h index 8a9804a4..05a0fd9f 100644 --- a/include/oonf/generic/dns_sd/dns_sd.h +++ b/include/oonf/generic/dns_sd/dns_sd.h @@ -1,7 +1,42 @@ -/** - * Copyright (C) 2019 Fraunhofer FKIE + +/* + * The olsr.org Optimized Link-State Routing daemon version 2 (olsrd2) + * Copyright (c) 2004-2019, the olsr.org team - see HISTORY file + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of olsr.org, olsrd nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * Visit http://www.olsr.org for more information. + * + * If you find this software useful feel free to make a donation + * to the project. For more information see the website or contact + * the copyright holders. * - * @author Henning Rogge */ /** @@ -17,15 +52,21 @@ #define OONF_DNS_SD_SUBSYSTEM "dns_sd" enum { + /*! maximum length of DNS service discovery prefix */ DNS_SD_PREFIX_LENGTH = 64, }; struct dns_sd_prefix { + /*! DNS service discovery prefix */ char dns_prefix[DNS_SD_PREFIX_LENGTH]; + /*! binary flag used for prefix detection in context */ uint64_t _flag; + + /*! usage counter of sd_prefix */ uint32_t _usage; + /*! node for tree in sd context */ struct avl_node _node; }; @@ -104,6 +145,13 @@ EXPORT struct avl_tree *dns_sd_get_context_tree(void); EXPORT struct dns_sd_context *dns_sd_context_get( const char *interface, const struct netaddr *ip); +/** + * Gets a specific prefix/hostname combination from a sd context + * @param context sd context + * @param hostname of service + * @param prefix sd prefix + * @return dns sd service, NULL if not there + */ static INLINE struct dns_sd_service * dns_sd_service_get(struct dns_sd_context *context, const char *hostname, struct dns_sd_prefix *prefix) { struct dns_sd_service *service; @@ -112,6 +160,12 @@ dns_sd_service_get(struct dns_sd_context *context, const char *hostname, struct return avl_find_element(&context->_service_tree, &key, service, _node); } +/** + * Gets the status of a specific prefix in a DNS SD service + * @param prefix dns sd prefix + * @param context dns sd context + * @returns service available, unavailable or unknown (query in progress) + */ static INLINE enum dns_sd_prefix_status dns_sd_context_has_prefix(struct dns_sd_prefix *prefix, struct dns_sd_context *context) { if ((context->available & prefix->_flag) != 0) { diff --git a/include/oonf/libcommon/netaddr.h b/include/oonf/libcommon/netaddr.h index 3abb5c09..4412a111 100644 --- a/include/oonf/libcommon/netaddr.h +++ b/include/oonf/libcommon/netaddr.h @@ -448,13 +448,21 @@ netaddr_socket_get_addressfamily(const union netaddr_socket *s) { return s->std.sa_family; } +/** + * @param sock netaddr socket + * @return binary address length of socket address + */ static INLINE size_t netaddr_socket_get_addr_binlength(const union netaddr_socket *sock) { return netaddr_get_af_maxprefix(sock->std.sa_family) >> 3; } +/** + * @param sock netaddr socket + * @return IPv6 socket scope (interface index), 0 if not available + */ static INLINE unsigned -netaddr_socket_get_scopex(const union netaddr_socket *sock) { +netaddr_socket_get_scope(const union netaddr_socket *sock) { return netaddr_socket_get_addressfamily(sock) == AF_INET6 ? sock->v6.sin6_scope_id : 0; } diff --git a/src/generic/dns_query/dns_query.c b/src/generic/dns_query/dns_query.c index 937d265c..b4d7dee2 100644 --- a/src/generic/dns_query/dns_query.c +++ b/src/generic/dns_query/dns_query.c @@ -22,12 +22,15 @@ #include #include +/* definitions */ #define LOG_DNS_QUERY _dns_query_subsystem.logging struct _dns_query_config { uint64_t timeout; }; +/* prototypes */ + static int _init(void); static void _cleanup(void); @@ -35,7 +38,7 @@ static void _cb_process_dns_query(struct oonf_socket_entry *entry); static void _cb_dns_timeout(struct oonf_timer_instance *timer); static void _cb_config_changed(void); -/* timeouts */ +/* timeout for DNS queries */ static struct oonf_timer_class _dns_timeout = { .name = "dns query timeout", .callback = _cb_dns_timeout @@ -74,17 +77,29 @@ static struct oonf_subsystem _dns_query_subsystem = { }; DECLARE_OONF_PLUGIN(_dns_query_subsystem); +/** + * constructor of dns query plugin + * @return always 0 + */ static int _init(void) { oonf_timer_add(&_dns_timeout); return 0; } +/** + * destructor of dns query plugin + */ static void _cleanup(void) { oonf_timer_remove(&_dns_timeout); } +/** + * Trigger a new DNS query + * @param q dns query + * @return 0 if query was triggered, error number otherwise + */ int dns_query_do(struct oonf_dns_query *q) { int error; @@ -127,6 +142,10 @@ dns_query_do(struct oonf_dns_query *q) { return 0; } +/** + * callback for processing DNS query responses + * @param entry socket entry + */ static void _cb_process_dns_query(struct oonf_socket_entry *entry) { struct oonf_dns_query *q; @@ -220,6 +239,10 @@ _cb_process_dns_query(struct oonf_socket_entry *entry) { } } +/** + * Callback for handling DNS timeouts + * @param timer timer instance + */ static void _cb_dns_timeout(struct oonf_timer_instance *timer) { struct oonf_dns_query *q; @@ -234,6 +257,9 @@ _cb_dns_timeout(struct oonf_timer_instance *timer) { } } +/** + * Callback for configuration changes + */ static void _cb_config_changed(void) { if (cfg_schema_tobin(&_config, _dns_query_section.post, _dns_query_entries, ARRAYSIZE(_dns_query_entries))) { diff --git a/src/generic/dns_sd/dns_sd.c b/src/generic/dns_sd/dns_sd.c index 633588a5..ecfae778 100644 --- a/src/generic/dns_sd/dns_sd.c +++ b/src/generic/dns_sd/dns_sd.c @@ -28,6 +28,7 @@ #include #include +/* definitions */ #define LOG_DNS_SD _dns_sd_subsystem.logging struct _dns_sd_config { @@ -38,6 +39,7 @@ enum sndsd_cfg { CFG_PREFIX }; +/* keys used for dnssd telnet command */ #define KEY_CONTEXT_IF "ctx_if" #define KEY_CONTEXT_IP "ctx_ip" #define KEY_CONTEXT_HOST "ctx_host" @@ -49,6 +51,7 @@ enum sndsd_cfg { #define KEY_SERVICE_IPV4 "service_ipv4" #define KEY_SERVICE_IPV6 "service_ipv6" +/* prototypes */ static int _init(void); static void _cleanup(void); @@ -78,7 +81,7 @@ static int _avl_comp_sd_context(const void *k1, const void *k2); static int _avl_comp_sd_service(const void *k1, const void *k2); static void _cb_config_changed(void); -/* telnet interface */ +/* telnet interface variables */ static char _value_ctx_if[IF_NAMESIZE]; static struct netaddr_str _value_ctx_ip; static char _value_ctx_host[512]; @@ -195,21 +198,28 @@ static struct oonf_class _sd_prefix_class = { .size = sizeof(struct dns_sd_prefix), }; +/* storage for sd context */ static struct oonf_class _sd_context_class = { .name = "sd context", .size = sizeof(struct dns_sd_context), }; +/* storage for sd service */ static struct oonf_class _sd_service_class = { .name = "sd result", .size = sizeof(struct dns_sd_service), }; +/* tree of prefixes */ static struct avl_tree _prefix_tree; + +/* tree of known context */ static struct avl_tree _context_tree; + +/* used binary flags for prefixes */ static uint64_t _used_flags = 0; -/* class extension for layer2 neighbor ips */ +/* callback defintiion for layer2 neighbor ips */ struct oonf_class_extension _l2neighip_ext = { .ext_name = "dns sd", .class_name = LAYER2_CLASS_NEIGHBOR_ADDRESS, @@ -226,6 +236,10 @@ static struct list_entity _update_list; /* tree of all dns-sd contexts */ static struct avl_tree _context_tree; +/** + * Constructor for dns sd plugin + * @return always 0 + */ static int _init(void) { oonf_class_extension_add(&_l2neighip_ext); @@ -250,6 +264,9 @@ _init(void) { return 0; } +/** + * Destructor of dns sd plugin + */ static void _cleanup(void) { struct dns_sd_prefix *prefix, *p_it; @@ -265,6 +282,10 @@ _cleanup(void) { oonf_class_remove(&_sd_service_class); } +/** + * Add a DNS prefix to the list to query + * @param name of DNS prefix + */ struct dns_sd_prefix * dns_sd_add(const char *name) { struct dns_sd_prefix *prefix; @@ -304,6 +325,10 @@ dns_sd_add(const char *name) { return prefix; } +/** + * Remove a DNS prefix from the list to query + * @param prefix DNS prefix + */ void dns_sd_remove(struct dns_sd_prefix *prefix) { struct dns_sd_context *context, *c_it; @@ -332,6 +357,12 @@ dns_sd_remove(struct dns_sd_prefix *prefix) { oonf_class_free(&_sd_prefix_class, prefix); } +/** + * Get a DNS context + * @param interface interface name of context + * @param ip host IP address of context (base for reverse DNS query) + * @return DNS sd context + */ struct dns_sd_context * dns_sd_context_get(const char *interface, const struct netaddr *ip) { struct dns_sd_context *context; @@ -343,16 +374,28 @@ dns_sd_context_get(const char *interface, const struct netaddr *ip) { return avl_find_element(dns_sd_get_context_tree(), &key, context, _global_node); } +/** + * @return DNS prefix tree + */ struct avl_tree * dns_sd_get_prefix_tree(void) { return &_prefix_tree; } +/** + * @return DNS context tree + */ struct avl_tree * dns_sd_get_context_tree(void) { return &_context_tree; } +/** + * Create a new DNS sd context + * @param ifname interface name for context + * @param ip IP address of context + * @return DNS sd context, NULL if out of memory + */ static struct dns_sd_context * _add_sd_context(const char *ifname, const struct netaddr *ip) { struct dns_sd_context *context; @@ -381,6 +424,10 @@ _add_sd_context(const char *ifname, const struct netaddr *ip) { return context; } +/** + * Remove an existing DNS sd context + * @param context DNS sd context + */ static void _remove_sd_context(struct dns_sd_context *context) { struct dns_sd_service *service, *s_it; @@ -393,6 +440,12 @@ _remove_sd_context(struct dns_sd_context *context) { oonf_class_free(&_sd_context_class, context); } +/** + * Create a new DNS sd service + * @param prefix registered DNS sd prefix + * @param hostname hostname of service + * @return DNS sd service, NULL if out of memory + */ static struct dns_sd_service * _add_sd_service(struct dns_sd_context *context, struct dns_sd_prefix *prefix, const char *hostname) { @@ -422,6 +475,10 @@ _add_sd_service(struct dns_sd_context *context, return service; } +/** + * Remove an existing DNS sd service + * @param context DNS sd context + */ static void _remove_sd_service(struct dns_sd_context *context, struct dns_sd_service *service) { free ((char *)service->key.hostname); @@ -433,6 +490,12 @@ _remove_sd_service(struct dns_sd_context *context, struct dns_sd_service *servic } } +/** + * Initialize a reverse DNS query + * @param dns dns query to initialize + * @param ip IP address the query is about + * @return 0 if initialization was successful, -1 otherwise + */ static int _get_rdns_arpa_name(struct dns_sd_query *dns, struct netaddr *ip) { static const char HEX[]="0123456789abcdef"; @@ -462,6 +525,11 @@ _get_rdns_arpa_name(struct dns_sd_query *dns, struct netaddr *ip) { return -1; } +/** + * Trigger the next DNS query for a DNS context + * @param dns dns sd query + * @return 0 if query was triggere, -1 otherwise + */ static int _work_on_l2neigh_addr(struct dns_sd_query *dns) { const struct os_interface_ip *if_ip; @@ -497,7 +565,7 @@ _work_on_l2neigh_addr(struct dns_sd_query *dns) { return -1; } netaddr_socket_init(&dns->client, &if_ip->address, 0, - netaddr_socket_get_scopex(&dns->server)); + netaddr_socket_get_scope(&dns->server)); if (!dns->context->hostname) { /* get hostname */ @@ -534,6 +602,9 @@ _work_on_l2neigh_addr(struct dns_sd_query *dns) { return -1; } +/** + * start a new DNS sd query for the next context in queue + */ static void _start_next_query(void) { struct dns_sd_context *context; @@ -582,6 +653,12 @@ _start_next_query(void) { } } +/** + * Add a new DNS context to the working queue, start new DNS query if + * the queue was empty + * @param ifname interface name of context + * @param ip IP address of context + */ static void _enqueue_dns_query(const char *ifname, const struct netaddr *ip) { struct dns_sd_context *context; @@ -608,6 +685,11 @@ _enqueue_dns_query(const char *ifname, const struct netaddr *ip) { _start_next_query(); } +/** + * Handle result of DNS A result (of DNS SRV query) + * @param q dns query + * @param response DNS A response + */ static void _cb_a_result(struct oonf_dns_query *q, struct dns_a *response) { struct dns_sd_query *dnssd_q; @@ -627,6 +709,11 @@ _cb_a_result(struct oonf_dns_query *q, struct dns_a *response) { netaddr_from_binary(addr, &response->addr, 4, AF_INET); } +/** + * Handle result of DNS AAAA result (of DNS SRV query) + * @param q dns query + * @param response DNS AAAA response + */ static void _cb_aaaa_result(struct oonf_dns_query *q, struct dns_aaaa *response) { struct dns_sd_query *dnssd_q; @@ -646,6 +733,11 @@ _cb_aaaa_result(struct oonf_dns_query *q, struct dns_aaaa *response) { netaddr_from_binary(addr, &response->addr, 16, AF_INET6); } +/** + * Handle result of DNS SRV result + * @param q dns query + * @param response DNS SRV response + */ static void _cb_srv_result(struct oonf_dns_query *q, struct dns_srv *response) { struct dns_sd_service *service; @@ -674,6 +766,11 @@ _cb_srv_result(struct oonf_dns_query *q, struct dns_srv *response) { } } +/** + * Handle result of DNS PTR result + * @param q dns query + * @param response DNS PTR response + */ static void _cb_ptr_result(struct oonf_dns_query *q, struct dns_ptr *response) { struct dns_sd_query *dnssd_q; @@ -687,6 +784,11 @@ _cb_ptr_result(struct oonf_dns_query *q, struct dns_ptr *response) { } } +/** + * Callback for handling end of a DNS query + * @param q dns query + * @param timeout true if a timeout happened, false if query was finished + */ static void _cb_query_done(struct oonf_dns_query *q, bool timeout) { struct dns_sd_query *dnssd_q; @@ -749,18 +851,33 @@ _cb_query_done(struct oonf_dns_query *q, bool timeout) { } } +/** + * Callback for dnssd telnet command + * @param con telnet connection + * @return telnet result + */ static enum oonf_telnet_result _cb_dnssd_cmd(struct oonf_telnet_data *con) { return oonf_viewer_telnet_handler( con->out, &_template_storage, OONF_DNS_SD_SUBSYSTEM, con->parameter, _templates, ARRAYSIZE(_templates)); } +/** + * Callback for dnssd telnet help command + * @param con telnet connection + * @return telnet result + */ static enum oonf_telnet_result _cb_dnssd_help(struct oonf_telnet_data *con) { return oonf_viewer_telnet_help( con->out, OONF_DNS_SD_SUBSYSTEM, con->parameter, _templates, ARRAYSIZE(_templates)); } +/** + * Create text output for DNS sd context + * @param template viewer template + * @return always 0 + */ static int _cb_create_text_context(struct oonf_viewer_template *template) { struct dns_sd_context *context; @@ -772,6 +889,11 @@ _cb_create_text_context(struct oonf_viewer_template *template) { return 0; } +/** + * Create text output for DNS sd service + * @param template viewer template + * @return always 0 + */ static int _cb_create_text_service(struct oonf_viewer_template *template) { struct dns_sd_context *context; @@ -788,6 +910,11 @@ _cb_create_text_service(struct oonf_viewer_template *template) { return 0; } +/** + * Create text output for DNS sd prefix + * @param template viewer template + * @return always 0 + */ static int _cb_create_text_prefix(struct oonf_viewer_template *template) { struct dns_sd_prefix *prefix; @@ -799,6 +926,10 @@ _cb_create_text_prefix(struct oonf_viewer_template *template) { return 0; } +/** + * Initialize output buffers for DNS sd context + * @param context DNS sd context + */ static void _initialize_context_values(struct dns_sd_context *context) { strscpy(_value_ctx_if, context->key.interface, IF_NAMESIZE); @@ -811,6 +942,10 @@ _initialize_context_values(struct dns_sd_context *context) { } } +/** + * Initialize output buffers for DNS sd service + * @param service DNS sd service + */ static void _initialize_service_values(struct dns_sd_service *service) { strscpy(_value_service_host, service->key.hostname, sizeof(_value_service_host)); @@ -822,6 +957,10 @@ _initialize_service_values(struct dns_sd_service *service) { netaddr_to_string(&_value_service_ipv6, &service->ipv6); } +/** + * Callback for handling new layer2 neighbor IPs + * @param ptr layer2 neighbor IP object + */ static void _cb_l2neighip_added(void *ptr) { struct oonf_layer2_neighbor_address *l2neigh_addr = ptr; @@ -838,6 +977,12 @@ _cb_l2neighip_added(void *ptr) { } } +/** + * AVL comparator for DNS sd context + * @param k1 key 1 + * @param k2 key 2 + * @return see memcmp() + */ static int _avl_comp_sd_context(const void *k1, const void *k2) { const struct dns_sd_context_key *ck1 = k1; @@ -846,6 +991,12 @@ _avl_comp_sd_context(const void *k1, const void *k2) { return memcmp(ck1, ck2, sizeof(*ck1)); } +/** + * AVL comparator for DNS sd service + * @param k1 key 1 + * @param k2 key 2 + * @return see memcmp() + */ static int _avl_comp_sd_service(const void *k1, const void *k2) { const struct dns_sd_service_key *sk1 = k1; @@ -859,6 +1010,9 @@ _avl_comp_sd_service(const void *k1, const void *k2) { return strcmp(sk1->prefix->dns_prefix, sk2->prefix->dns_prefix); } +/** + * Callback for configuration changes + */ static void _cb_config_changed(void) { struct dns_sd_prefix *sd_prefix; diff --git a/src/generic/layer2info/layer2info.c b/src/generic/layer2info/layer2info.c index ff31d224..ab8a2cf7 100644 --- a/src/generic/layer2info/layer2info.c +++ b/src/generic/layer2info/layer2info.c @@ -71,12 +71,13 @@ static void _cleanup(void); static enum oonf_telnet_result _cb_layer2info(struct oonf_telnet_data *con); static enum oonf_telnet_result _cb_layer2info_help(struct oonf_telnet_data *con); -static void _initialize_if_data_values(struct oonf_viewer_template *template, struct oonf_layer2_data *data); +static void _initialize_if_data_values(struct oonf_viewer_template *template, struct oonf_layer2_net *l2net); +static void _initialize_if_default_data_values(struct oonf_viewer_template *template, struct oonf_layer2_net *l2net); static void _initialize_if_origin_values(struct oonf_layer2_data *data); static void _initialize_if_values(struct oonf_layer2_net *net); static void _initialize_if_ip_values(struct oonf_layer2_peer_address *peer_ip); -static void _initialize_neigh_data_values(struct oonf_viewer_template *template, struct oonf_layer2_data *data); +static void _initialize_neigh_data_values(struct oonf_viewer_template *template, struct oonf_layer2_neigh *l2neigh); static void _initialize_neigh_origin_values(struct oonf_layer2_data *data); static void _initialize_neigh_values(struct oonf_layer2_neigh *neigh); static void _initialize_neigh_ip_values(struct oonf_layer2_neighbor_address *neigh_addr); @@ -510,16 +511,32 @@ _initialize_if_ip_values(struct oonf_layer2_peer_address *peer_ip) { /** * Initialize the value buffers for an array of layer2 data objects * @param template viewer template - * @param data array of data objects + * @param l2net layer-2 network data */ static void -_initialize_if_data_values(struct oonf_viewer_template *template, struct oonf_layer2_data *data) { +_initialize_if_data_values(struct oonf_viewer_template *template, struct oonf_layer2_net *l2net) { size_t i; memset(_value_if_data, 0, sizeof(_value_if_data)); for (i = 0; i < OONF_LAYER2_NET_COUNT; i++) { - oonf_layer2_net_data_to_string(_value_if_data[i], sizeof(_value_if_data[i]), &data[i], i, template->create_raw); + oonf_layer2_net_data_to_string(_value_if_data[i], sizeof(_value_if_data[i]), l2net, i, template->create_raw); + } +} + +/** + * Initialize the value buffers for an array of layer2 data objects + * @param template viewer template + * @param l2net layer-2 network data + */ +static void +_initialize_if_default_data_values(struct oonf_viewer_template *template, struct oonf_layer2_net *l2net) { + size_t i; + + memset(_value_if_data, 0, sizeof(_value_if_data)); + + for (i = 0; i < OONF_LAYER2_NET_COUNT; i++) { + oonf_layer2_net_default_data_to_string(_value_if_data[i], sizeof(_value_if_data[i]), l2net, i, template->create_raw); } } @@ -576,17 +593,17 @@ _initialize_neigh_ip_values(struct oonf_layer2_neighbor_address *neigh_addr) { /** * Initialize the value buffers for an array of layer2 data objects * @param template viewer template - * @param data array of data objects + * @param l2neigh layer-2 neighbor object */ static void -_initialize_neigh_data_values(struct oonf_viewer_template *template, struct oonf_layer2_data *data) { +_initialize_neigh_data_values(struct oonf_viewer_template *template, struct oonf_layer2_neigh *l2neigh) { size_t i; memset(_value_neigh_data, 0, sizeof(_value_neigh_data)); for (i = 0; i < OONF_LAYER2_NEIGH_COUNT; i++) { oonf_layer2_neigh_data_to_string( - _value_neigh_data[i], sizeof(_value_neigh_data[i]), &data[i], i, template->create_raw); + _value_neigh_data[i], sizeof(_value_neigh_data[i]), l2neigh, i, template->create_raw); } } @@ -641,7 +658,7 @@ _cb_create_text_interface(struct oonf_viewer_template *template) { avl_for_each_element(oonf_layer2_get_net_tree(), net, _node) { _initialize_if_values(net); - _initialize_if_data_values(template, net->data); + _initialize_if_data_values(template, net); _initialize_if_origin_values(net->data); /* generate template output */ @@ -688,7 +705,7 @@ _cb_create_text_neighbor(struct oonf_viewer_template *template) { avl_for_each_element(&net->neighbors, neigh, _node) { _initialize_neigh_values(neigh); - _initialize_neigh_data_values(template, neigh->data); + _initialize_neigh_data_values(template, neigh); _initialize_neigh_origin_values(neigh->data); /* generate template output */ @@ -738,7 +755,7 @@ _cb_create_text_default(struct oonf_viewer_template *template) { avl_for_each_element(oonf_layer2_get_net_tree(), net, _node) { _initialize_if_values(net); - _initialize_neigh_data_values(template, net->neighdata); + _initialize_if_default_data_values(template, net); _initialize_neigh_origin_values(net->neighdata); /* generate template output */ From d2ef771da391a807529a81d7e7291703caf36a79 Mon Sep 17 00:00:00 2001 From: Henning Rogge Date: Thu, 16 May 2019 13:11:13 +0200 Subject: [PATCH 19/63] Add support for small isoprefixes on string parsing --- src/libcommon/isonumber.c | 20 +++++++---- src/tests/common/test_common_isonumber.c | 46 ++++++++++++++++++++---- 2 files changed, 53 insertions(+), 13 deletions(-) diff --git a/src/libcommon/isonumber.c b/src/libcommon/isonumber.c index 0af6fe4d..6c96d044 100644 --- a/src/libcommon/isonumber.c +++ b/src/libcommon/isonumber.c @@ -155,6 +155,7 @@ isonumber_to_s64(int64_t *dst, const char *iso, uint64_t scaling) { int isonumber_to_u64(uint64_t *dst, const char *iso, uint64_t scaling) { static const char symbol_large[] = " kMGTPE"; + static const char symbol_small[] = " munpfa"; uint64_t num, fraction_scale, factor; char *next = NULL, *prefix; @@ -204,13 +205,20 @@ isonumber_to_u64(uint64_t *dst, const char *iso, uint64_t scaling) { } prefix = strchr(symbol_large, next[0]); - if (!prefix) { - return -1; + if (prefix) { + while (prefix > symbol_large) { + factor *= 1000; + prefix--; + } } - - while (prefix > symbol_large) { - factor *= 1000; - prefix--; + else { + prefix = strchr(symbol_small, next[0]); + if (prefix) { + while (prefix > symbol_small) { + fraction_scale *= 1000; + prefix--; + } + } } } diff --git a/src/tests/common/test_common_isonumber.c b/src/tests/common/test_common_isonumber.c index d2cb3bd9..fb1424b9 100644 --- a/src/tests/common/test_common_isonumber.c +++ b/src/tests/common/test_common_isonumber.c @@ -91,7 +91,7 @@ test_str_from_isonumber_u64(void) { } static void -test_isonumber_to_u64_to_string(void) { +test_isonumber_to_u64_from_string(void) { static const char *tests[] = { "1.0", "1k", "1.024k", "1M", "1.024M", "1.023k" }; @@ -119,14 +119,14 @@ test_isonumber_to_u64_to_string(void) { } static void -test_isonumber_to_s64_to_string(void) { +test_isonumber_to_s64_from_string(void) { static const char *tests[] = { "1k", "1.024k", "1M", "1.024M", "1.023k", - "-1k", "-1.024k", "-1M", "-1.024M", "-1.023k" + "-1k", "-1.024k", "-1M", "-1.024M", "-1.023k", }; static int64_t results[] = { 1000, 1024, 1000*1000, 1000*1024, 1023, - -1000, -1024, -1000*1000, -1000*1024, -1023 + -1000, -1024, -1000*1000, -1000*1024, -1023, }; size_t i; @@ -148,6 +148,37 @@ test_isonumber_to_s64_to_string(void) { END_TEST(); } +static void +test_isonumber_to_s64_from_string_2(void) { + static const char *tests[] = { + "1m", "1.024m", "1u", "1.024u", "1.023m", "1n", + "-1m", "-1.024m", "-1u", "-1.024u", "-1.023m", "-1n", + }; + static int64_t results[] = { + 1000000, 1024000, 1000, 1024, 1023000, 1, + -1000000, -1024000, -1000, -1024, -1023000, -1, + }; + + size_t i; + + int64_t result; + int tmp; + + START_TEST(); + + for (i=0; i Date: Thu, 16 May 2019 13:27:34 +0200 Subject: [PATCH 20/63] Fix error case for unknown iso prefix --- src/libcommon/isonumber.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/libcommon/isonumber.c b/src/libcommon/isonumber.c index 6c96d044..59585d54 100644 --- a/src/libcommon/isonumber.c +++ b/src/libcommon/isonumber.c @@ -219,6 +219,9 @@ isonumber_to_u64(uint64_t *dst, const char *iso, uint64_t scaling) { prefix--; } } + else { + return -1; + } } } From fe0e783134f73f14b8ec145f1fb57b6b873cd2ea Mon Sep 17 00:00:00 2001 From: Henning Rogge Date: Thu, 16 May 2019 13:27:49 +0200 Subject: [PATCH 21/63] Fix testcase for isoprefix, 'a' is now a valid prefix --- src/tests/config/test_config_validation.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/tests/config/test_config_validation.c b/src/tests/config/test_config_validation.c index f271f80f..9ca49b5b 100644 --- a/src/tests/config/test_config_validation.c +++ b/src/tests/config/test_config_validation.c @@ -212,15 +212,15 @@ test_validate_int_miss(void) { CHECK_TRUE(0 == cfg_schema_validate(db, false, false, &out), "%s", abuf_getptr(&out)); - cfg_db_add_entry(db, CFG_SEC, CFG_SECNAME, "int", "a"); + cfg_db_add_entry(db, CFG_SEC, CFG_SECNAME, "int", "b"); CHECK_TRUE(0 != cfg_schema_validate(db, false, false, NULL), "validation missed bad integer"); - cfg_db_add_entry(db, CFG_SEC, CFG_SECNAME, "int", "1a"); + cfg_db_add_entry(db, CFG_SEC, CFG_SECNAME, "int", "1b"); CHECK_TRUE(0 != cfg_schema_validate(db, false, false, NULL), "validation missed bad integer"); - cfg_db_add_entry(db, CFG_SEC, CFG_SECNAME, "int", "1 a"); + cfg_db_add_entry(db, CFG_SEC, CFG_SECNAME, "int", "1 b"); CHECK_TRUE(0 != cfg_schema_validate(db, false, false, NULL), "validation missed bad integer"); @@ -272,7 +272,7 @@ test_validate_fractional_miss(void) { CHECK_TRUE(0 == cfg_schema_validate(db, false, false, &out), "%s", abuf_getptr(&out)); - cfg_db_add_entry(db, CFG_SEC, CFG_SECNAME, "fractional", "a"); + cfg_db_add_entry(db, CFG_SEC, CFG_SECNAME, "fractional", "b"); CHECK_TRUE(0 != cfg_schema_validate(db, false, false, NULL), "validation missed bad integer"); From 29d95461c79e09e1abe7de36356db802cf3ca517 Mon Sep 17 00:00:00 2001 From: Henning Rogge Date: Wed, 22 May 2019 08:29:47 +0200 Subject: [PATCH 22/63] Add better support for prefix filter in layer2 import and layer2 export --- src/generic/layer2_export/layer2_export.c | 57 +++++++++++++++++++---- src/generic/layer2_import/layer2_import.c | 38 +++++++++------ 2 files changed, 74 insertions(+), 21 deletions(-) diff --git a/src/generic/layer2_export/layer2_export.c b/src/generic/layer2_export/layer2_export.c index 767a30e7..e434d039 100644 --- a/src/generic/layer2_export/layer2_export.c +++ b/src/generic/layer2_export/layer2_export.c @@ -64,6 +64,15 @@ struct _l2export_data { /*! originator to import, defined as the section name */ char originator[16]; + /*! address filter */ + struct netaddr_acl filter; + + /*! filter by prefix length */ + int32_t min_prefix_length; + + /*! filter by prefix length */ + int32_t max_prefix_length; + /*! fib distance */ int32_t fib_distance; @@ -120,7 +129,7 @@ static void _initiate_shutdown(void); static struct _l2export_data *_get_l2export(const char *name); static void _destroy_l2export(struct _l2export_data *); -static bool _is_matching_origin(struct oonf_layer2_neighbor_address *, const char *pattern); +static bool _is_matching_route(struct oonf_layer2_neighbor_address *, struct _l2export_data *); static struct _l2export_route *_get_route(struct _l2export_data *data, struct os_route_key *key); static void _destroy_route(struct _l2export_route *route); @@ -132,6 +141,13 @@ static void _cb_l2neigh_ip_removed(void *); static void _cb_cfg_changed(void); static struct cfg_schema_entry _l2export_entries[] = { + CFG_MAP_ACL(_l2export_data, filter, "matches", ACL_DEFAULT_ACCEPT, + "Ip addresses the filter should be applied to" + " (the plugin will never import loopback, linklocal or multicast IPs)"), + CFG_MAP_INT32_MINMAX(_l2export_data, min_prefix_length, "min_prefix_length", "0", + "Minimal acceptable prefix length to import", 0, 0, 128), + CFG_MAP_INT32_MINMAX(_l2export_data, max_prefix_length, "max_prefix_length", "128", + "Maximum acceptable prefix length to import", 0, 0, 128), CFG_MAP_INT32_MINMAX(_l2export_data, fib_distance, "fib_distance", "2", "fib distance for exported layer2 entries", 0, 1, 255), CFG_MAP_INT32_MINMAX(_l2export_data, fib_table, "fib_table", "254", @@ -273,6 +289,9 @@ _destroy_l2export(struct _l2export_data *l2export) { _destroy_route(l2route); } + /* free filter resources */ + netaddr_acl_remove(&l2export->filter); + /* first remove the import settings from the tree */ avl_remove(&_l2export_tree, &l2export->_node); @@ -286,19 +305,41 @@ _destroy_l2export(struct _l2export_data *l2export) { * @return true if matching, false otherwise */ static bool -_is_matching_origin(struct oonf_layer2_neighbor_address *addr, const char *pattern) { +_is_matching_route(struct oonf_layer2_neighbor_address *addr, struct _l2export_data *data) { int len; +#ifdef OONF_LOG_DEBUG_INFO + struct netaddr_str nbuf; +#endif + + /* check prefix length */ + if (data->min_prefix_length > netaddr_get_prefix_length(&addr->ip)) { + OONF_DEBUG(LOG_L2EXPORT, "prefix length %u is too small (filter was %d)", + netaddr_get_prefix_length(&addr->ip), data->min_prefix_length); + return false; + } + if (data->max_prefix_length < netaddr_get_prefix_length(&addr->ip)) { + OONF_DEBUG(LOG_L2EXPORT, "prefix length %u is too large (filter was %d)", + netaddr_get_prefix_length(&addr->ip), data->max_prefix_length); + return false; + } + /* check if destination matches */ + if (!netaddr_acl_check_accept(&data->filter, &addr->ip)) { + OONF_DEBUG(LOG_L2EXPORT, "Bad prefix %s", netaddr_to_string(&nbuf, &addr->ip)); + return false; + } - if (strcmp(addr->origin->name, pattern) == 0) { + /* check if originator name is exact match */ + if (strcmp(addr->origin->name, data->originator) == 0) { return true; } - len = strlen(pattern); - if (len == 0 || pattern[len-1] != '*') { + /* check if originator has wildcard and is a prefix match */ + len = strlen(data->originator); + if (len == 0 || data->originator[len-1] != '*') { return false; } - return strncmp(addr->origin->name, pattern, len-1) == 0; + return strncmp(addr->origin->name, data->originator, len-1) == 0; } /** @@ -428,7 +469,7 @@ _cb_l2neigh_ip_added(void *ptr) { avl_for_each_element(&_l2export_tree, l2export, _node) { OONF_DEBUG(LOG_L2EXPORT, "Check export %s against originator %s", l2export->originator, nip->origin->name); - if (_is_matching_origin(nip, l2export->originator)) { + if (_is_matching_route(nip, l2export)) { OONF_DEBUG(LOG_L2EXPORT, "match"); l2route = _get_route(l2export, &rt_key); if (!l2route) { @@ -475,7 +516,7 @@ _cb_l2neigh_ip_removed(void *ptr) { avl_for_each_element(&_l2export_tree, l2export, _node) { OONF_DEBUG(LOG_L2EXPORT, "Check export %s against originator %s", l2export->originator, nip->origin->name); - if (_is_matching_origin(nip, l2export->originator)) { + if (_is_matching_route(nip, l2export)) { OONF_DEBUG(LOG_L2EXPORT, "match"); l2route = avl_find_element(&l2export->route_tree, &rt_key, l2route, _node); if (l2route) { diff --git a/src/generic/layer2_import/layer2_import.c b/src/generic/layer2_import/layer2_import.c index 0c1c1302..6d50b42f 100644 --- a/src/generic/layer2_import/layer2_import.c +++ b/src/generic/layer2_import/layer2_import.c @@ -81,8 +81,11 @@ struct _import_entry { /*! address filter */ struct netaddr_acl filter; - /*! filter by prefix length, -1 to ignore */ - int32_t prefix_length; + /*! filter by prefix length */ + int32_t min_prefix_length; + + /*! filter by prefix length */ + int32_t max_prefix_length; /*! filter by interface name, length null to ignore*/ char ifname[IF_NAMESIZE]; @@ -137,8 +140,10 @@ static struct cfg_schema_entry _l2_entries[] = { CFG_MAP_ACL(_import_entry, filter, "matches", ACL_DEFAULT_ACCEPT, "Ip addresses the filter should be applied to" " (the plugin will never import loopback, linklocal or multicast IPs)"), - CFG_MAP_INT32_MINMAX(_import_entry, prefix_length, "prefix_length", "-1", - "Prefix length the filter should be applied to, -1 for any prefix length", 0, -1, 128), + CFG_MAP_INT32_MINMAX(_import_entry, min_prefix_length, "min_prefix_length", "0", + "Minimal acceptable prefix length to import", 0, 0, 128), + CFG_MAP_INT32_MINMAX(_import_entry, max_prefix_length, "max_prefix_length", "128", + "Maximum acceptable prefix length to import", 0, 0, 128), CFG_MAP_STRING_ARRAY( _import_entry, ifname, "interface", "", "Interface name of matching routes, empty if all interfaces", IF_NAMESIZE), CFG_MAP_INT32_MINMAX( @@ -161,8 +166,10 @@ static struct cfg_schema_entry _lan_entries[] = { CFG_MAP_ACL(_import_entry, filter, "matches", ACL_DEFAULT_ACCEPT, "Ip addresses the filter should be applied to" " (the plugin will never import loopback, linklocal or multicast IPs)"), - CFG_MAP_INT32_MINMAX(_import_entry, prefix_length, "prefix_length", "-1", - "Prefix length the filter should be applied to, -1 for any prefix length", 0, -1, 128), + CFG_MAP_INT32_MINMAX(_import_entry, min_prefix_length, "min_prefix_length", "0", + "Minimal acceptable prefix length to import", 0, 0, 128), + CFG_MAP_INT32_MINMAX(_import_entry, max_prefix_length, "max_prefix_length", "128", + "Maximum acceptable prefix length to import", 0, 0, 128), CFG_MAP_STRING_ARRAY( _import_entry, ifname, "interface", "", "Interface name of matching routes, empty if all interfaces", IF_NAMESIZE), CFG_MAP_INT32_MINMAX( @@ -405,15 +412,14 @@ _cb_rt_event(const struct os_route *route, bool set) { } /* check prefix length */ - if (import->prefix_length != -1 && import->prefix_length != netaddr_get_prefix_length(&route->p.key.dst)) { - OONF_DEBUG(LOG_L2_IMPORT, "Bad prefix length %u (filter was %d)", - netaddr_get_prefix_length(&route->p.key.dst), import->prefix_length); + if (import->min_prefix_length > netaddr_get_prefix_length(&route->p.key.dst)) { + OONF_DEBUG(LOG_L2_IMPORT, "prefix length %u is too small (filter was %d)", + netaddr_get_prefix_length(&route->p.key.dst), import->min_prefix_length); continue; } - - /* check if destination matches */ - if (!netaddr_acl_check_accept(&import->filter, &route->p.key.dst)) { - OONF_DEBUG(LOG_L2_IMPORT, "Bad prefix %s", netaddr_to_string(&nbuf, &route->p.key.dst)); + if (import->max_prefix_length < netaddr_get_prefix_length(&route->p.key.dst)) { + OONF_DEBUG(LOG_L2_IMPORT, "prefix length %u is too large (filter was %d)", + netaddr_get_prefix_length(&route->p.key.dst), import->max_prefix_length); continue; } @@ -447,6 +453,12 @@ _cb_rt_event(const struct os_route *route, bool set) { } } + /* check if destination matches */ + if (!netaddr_acl_check_accept(&import->filter, &route->p.key.dst)) { + OONF_DEBUG(LOG_L2_IMPORT, "Bad prefix %s", netaddr_to_string(&nbuf, &route->p.key.dst)); + continue; + } + /* see if user wants to overwrite layer2 network name */ if (import->fixed_l2if_name[0]) { l2ifname = import->fixed_l2if_name; From 743224886bf3d81cf51abea2ad18cbabf871f94f Mon Sep 17 00:00:00 2001 From: Henning Rogge Date: Wed, 22 May 2019 08:30:36 +0200 Subject: [PATCH 23/63] Unify dlep radio and router plugin and add telnet interface --- apps/dlep-radio/CMakeLists.txt | 2 +- apps/dlep-router/CMakeLists.txt | 2 +- apps/olsrd2-dlep/CMakeLists.txt | 2 +- include/oonf/base/oonf_clock.h | 14 +- .../dlep/{radio/dlep_radio.h => dlep.h} | 9 +- include/oonf/generic/dlep/dlep_interface.h | 3 +- ...dlep_router_internal.h => dlep_internal.h} | 15 +- include/oonf/generic/dlep/dlep_session.h | 9 + .../{router/dlep_router.h => dlep_telnet.h} | 11 +- .../generic/dlep/radio/dlep_radio_internal.h | 54 ----- .../generic/dlep/router/dlep_router_session.h | 3 - src/generic/dlep/CMakeLists.txt | 55 ++--- src/generic/dlep/README_DLEP_RADIO | 4 +- src/generic/dlep/README_DLEP_ROUTER | 4 +- .../dlep/{radio/dlep_radio.c => dlep.c} | 153 +++++++++++- src/generic/dlep/dlep_session.c | 5 + src/generic/dlep/dlep_telnet.c | 229 ++++++++++++++++++ src/generic/dlep/ext_base_proto/proto_radio.c | 8 +- src/generic/dlep/radio/dlep_radio_interface.c | 23 +- src/generic/dlep/radio/dlep_radio_session.c | 17 +- src/generic/dlep/router/dlep_router.c | 229 ------------------ .../dlep/router/dlep_router_interface.c | 19 +- src/generic/dlep/router/dlep_router_session.c | 18 +- 23 files changed, 483 insertions(+), 405 deletions(-) rename include/oonf/generic/dlep/{radio/dlep_radio.h => dlep.h} (94%) rename include/oonf/generic/dlep/{router/dlep_router_internal.h => dlep_internal.h} (86%) rename include/oonf/generic/dlep/{router/dlep_router.h => dlep_telnet.h} (92%) delete mode 100644 include/oonf/generic/dlep/radio/dlep_radio_internal.h rename src/generic/dlep/{radio/dlep_radio.c => dlep.c} (58%) create mode 100644 src/generic/dlep/dlep_telnet.c delete mode 100644 src/generic/dlep/router/dlep_router.c diff --git a/apps/dlep-radio/CMakeLists.txt b/apps/dlep-radio/CMakeLists.txt index 4e887c21..13fddf3b 100644 --- a/apps/dlep-radio/CMakeLists.txt +++ b/apps/dlep-radio/CMakeLists.txt @@ -48,7 +48,7 @@ IF (NOT OONF_STATIC_PLUGINS) remotecontrol layer2_config layer2_import - dlep_radio + dlep ) ENDIF (NOT OONF_STATIC_PLUGINS) diff --git a/apps/dlep-router/CMakeLists.txt b/apps/dlep-router/CMakeLists.txt index 5292aeed..5c5aed2f 100644 --- a/apps/dlep-router/CMakeLists.txt +++ b/apps/dlep-router/CMakeLists.txt @@ -50,7 +50,7 @@ IF (NOT OONF_STATIC_PLUGINS) layer2_export dns_query dns_sd - dlep_router + dlep ) ENDIF (NOT OONF_STATIC_PLUGINS) diff --git a/apps/olsrd2-dlep/CMakeLists.txt b/apps/olsrd2-dlep/CMakeLists.txt index 16156f16..f300af02 100644 --- a/apps/olsrd2-dlep/CMakeLists.txt +++ b/apps/olsrd2-dlep/CMakeLists.txt @@ -58,7 +58,7 @@ IF (NOT OONF_STATIC_PLUGINS) layer2_import auto_ll4 http - dlep_router + dlep ) ENDIF (NOT OONF_STATIC_PLUGINS) diff --git a/include/oonf/base/oonf_clock.h b/include/oonf/base/oonf_clock.h index 66d04d03..d76a0894 100644 --- a/include/oonf/base/oonf_clock.h +++ b/include/oonf/base/oonf_clock.h @@ -176,6 +176,18 @@ EXPORT uint64_t oonf_clock_getNow(void); EXPORT const char *oonf_clock_toClockString(struct isonumber_str *, uint64_t); +/** + * Converts an internal time interval into a string representation with + * the numbers of seconds (including milliseconds as fractions) + * @param buf target buffer + * @param i time value + * @return pointer to string representation + */ +static INLINE const char * +oonf_clock_toIntervalString_ext(struct isonumber_str *buf, int64_t i, bool raw) { + return isonumber_from_s64(buf, i, "", 1000, raw); +} + /** * Converts an internal time interval into a string representation with * the numbers of seconds (including milliseconds as fractions) @@ -185,7 +197,7 @@ EXPORT const char *oonf_clock_toClockString(struct isonumber_str *, uint64_t); */ static INLINE const char * oonf_clock_toIntervalString(struct isonumber_str *buf, int64_t i) { - return isonumber_from_s64(buf, i, "", 1000, true); + return oonf_clock_toIntervalString_ext(buf, i, false); } /** diff --git a/include/oonf/generic/dlep/radio/dlep_radio.h b/include/oonf/generic/dlep/dlep.h similarity index 94% rename from include/oonf/generic/dlep/radio/dlep_radio.h rename to include/oonf/generic/dlep/dlep.h index 2d9aadfd..d2807d76 100644 --- a/include/oonf/generic/dlep/radio/dlep_radio.h +++ b/include/oonf/generic/dlep/dlep.h @@ -43,10 +43,11 @@ * @file */ -#ifndef DLEP_RADIO_H_ -#define DLEP_RADIO_H_ +#ifndef DLEP_H_ +#define DLEP_H_ /*! subsystem identifier */ -#define OONF_DLEP_RADIO_SUBSYSTEM "dlep_radio" +#define OONF_DLEP_SUBSYSTEM "dlep" + +#endif /* DLEP_H_ */ -#endif /* DLEP_RADIO_H_ */ diff --git a/include/oonf/generic/dlep/dlep_interface.h b/include/oonf/generic/dlep/dlep_interface.h index 3ac5e230..8a8f30b6 100644 --- a/include/oonf/generic/dlep/dlep_interface.h +++ b/include/oonf/generic/dlep/dlep_interface.h @@ -50,6 +50,7 @@ #include #include #include +#include #define DLEP_IF_UDP_NONE_STR "none" #define DLEP_IF_UDP_SINGLE_SESSION_STR "single_session" @@ -91,7 +92,7 @@ struct dlep_if { /*! true if this interface is used for DLEP radio */ bool radio; - /*! hook into session tree, interface name is the key */ + /*! hook into interface tree, interface name is the key */ struct avl_node _node; /*! tree of all sessions of same type (radio/router) on this interface */ diff --git a/include/oonf/generic/dlep/router/dlep_router_internal.h b/include/oonf/generic/dlep/dlep_internal.h similarity index 86% rename from include/oonf/generic/dlep/router/dlep_router_internal.h rename to include/oonf/generic/dlep/dlep_internal.h index cf17fa43..1c553ee1 100644 --- a/include/oonf/generic/dlep/router/dlep_router_internal.h +++ b/include/oonf/generic/dlep/dlep_internal.h @@ -43,12 +43,19 @@ * @file */ -#ifndef DLEP_ROUTER_INTERNAL_H_ -#define DLEP_ROUTER_INTERNAL_H_ +#ifndef DLEP_INTERNAL_H_ +#define DLEP_INTERNAL_H_ #include -/* headers only for use inside the DLEP_ROUTER subsystem */ +/* headers only for use inside the generic DLEP code */ +enum oonf_log_source LOG_DLEP; + +/* headers only for use inside the DLEP_RADIO code*/ +enum oonf_log_source LOG_DLEP_RADIO; + +/* headers only for use inside the DLEP_ROUTER code*/ enum oonf_log_source LOG_DLEP_ROUTER; -#endif /* DLEP_ROUTER_INTERNAL_H_ */ +#endif /* DLEP_INTERNAL_H_ */ + diff --git a/include/oonf/generic/dlep/dlep_session.h b/include/oonf/generic/dlep/dlep_session.h index b6473290..2c49c4dc 100644 --- a/include/oonf/generic/dlep/dlep_session.h +++ b/include/oonf/generic/dlep/dlep_session.h @@ -336,9 +336,15 @@ struct dlep_session { /*! rate of remote heartbeats */ uint64_t remote_heartbeat_interval; + /*! local endpoint of current communication */ + union netaddr_socket local_socket; + /*! remote endpoint of current communication */ union netaddr_socket remote_socket; + /*! timestamp when the session was established */ + uint64_t activation_time; + /*! timeout for acknowledgement signal */ struct oonf_timer_instance _ack_timeout; @@ -347,6 +353,9 @@ struct dlep_session { /*! session data for IP extension */ struct dlep_session_ext_ip _ext_ip; + + /*! remember all streams bound to an interface */ + struct avl_node _node; }; void dlep_session_init(void); diff --git a/include/oonf/generic/dlep/router/dlep_router.h b/include/oonf/generic/dlep/dlep_telnet.h similarity index 92% rename from include/oonf/generic/dlep/router/dlep_router.h rename to include/oonf/generic/dlep/dlep_telnet.h index f0e38452..2b9a5ee0 100644 --- a/include/oonf/generic/dlep/router/dlep_router.h +++ b/include/oonf/generic/dlep/dlep_telnet.h @@ -43,10 +43,11 @@ * @file */ -#ifndef DLEP_ROUTER_H_ -#define DLEP_ROUTER_H_ +#ifndef DLEP_TELNET_H_ +#define DLEP_TELNET_H_ -/*! subsystem identifier */ -#define OONF_DLEP_ROUTER_SUBSYSTEM "dlep_router" +int dlep_telnet_init(void); +void dlep_telnet_cleanup(void); + +#endif /* DLEP_TELNET_H_ */ -#endif /* DLEP_ROUTER_H_ */ diff --git a/include/oonf/generic/dlep/radio/dlep_radio_internal.h b/include/oonf/generic/dlep/radio/dlep_radio_internal.h deleted file mode 100644 index 12e111c6..00000000 --- a/include/oonf/generic/dlep/radio/dlep_radio_internal.h +++ /dev/null @@ -1,54 +0,0 @@ - -/* - * The olsr.org Optimized Link-State Routing daemon version 2 (olsrd2) - * Copyright (c) 2004-2015, the olsr.org team - see HISTORY file - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * * Neither the name of olsr.org, olsrd nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS - * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE - * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN - * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - * Visit http://www.olsr.org for more information. - * - * If you find this software useful feel free to make a donation - * to the project. For more information see the website or contact - * the copyright holders. - * - */ - -/** - * @file - */ - -#ifndef DLEP_RADIO_INTERNAL_H_ -#define DLEP_RADIO_INTERNAL_H_ - -#include - -/* headers only for use inside the DLEP_RADIO subsystem */ -enum oonf_log_source LOG_DLEP_RADIO; - -#endif /* DLEP_RADIO_INTERNAL_H_ */ diff --git a/include/oonf/generic/dlep/router/dlep_router_session.h b/include/oonf/generic/dlep/router/dlep_router_session.h index b8e82bef..feb83393 100644 --- a/include/oonf/generic/dlep/router/dlep_router_session.h +++ b/include/oonf/generic/dlep/router/dlep_router_session.h @@ -85,9 +85,6 @@ struct dlep_router_session { /*! back pointer to interface session */ struct dlep_router_if *interface; - - /*! remember all streams bound to an interface */ - struct avl_node _node; }; void dlep_router_session_init(void); diff --git a/src/generic/dlep/CMakeLists.txt b/src/generic/dlep/CMakeLists.txt index 33fde4a3..2f533c5a 100644 --- a/src/generic/dlep/CMakeLists.txt +++ b/src/generic/dlep/CMakeLists.txt @@ -1,48 +1,35 @@ # set source files -SET (radio_only_source ext_base_proto/proto_radio.c - radio/dlep_radio.c - radio/dlep_radio_interface.c - radio/dlep_radio_session.c) -SET (router_only_source ext_base_proto/proto_router.c - router/dlep_router.c - router/dlep_router_interface.c - router/dlep_router_session.c) -SET (common_source ext_base_metric/metric.c +SET (source ext_base_metric/metric.c ext_base_ip/ip.c ext_base_proto/proto.c + ext_base_proto/proto_router.c + ext_base_proto/proto_radio.c ext_l1_statistics/l1_statistics.c ext_l2_statistics/l2_statistics.c ext_radio_attributes/radio_attributes.c ext_lid/lid.c ext_dns/dns.c + router/dlep_router_interface.c + router/dlep_router_session.c + radio/dlep_radio_interface.c + radio/dlep_radio_session.c dlep_extension.c dlep_interface.c - dlep_session.c dlep_reader.c - dlep_writer.c) - -# sources/includes for dlep_radio -SET (radio_source ${common_source} - ${radio_only_source}) -SET (radio_include radio/dlep_radio.h) - -# sources/includes for dlep_router -SET (router_source ${common_source} - ${router_only_source}) -SET (router_include router/dlep_router.h) + dlep_session.c + dlep_telnet.c + dlep_writer.c + dlep.c) -# sources/includes for dlep_proxy -SET (proxy_source ${common_source} - ${radio_only_source} - ${router_only_source}) -SET (proxy_include ${radio_include} - ${router_include}) +# sources/includes for dlep +SET (include dlep_extension.h + dlep_iana.h + dlep_interface.h + dlep_reader.h + dlep_session.h + dlep_telnet.h + dlep_writer.h + dlep.h) # use generic plugin maker for dlep-radio -oonf_create_plugin("dlep_radio" "${radio_source}" "${radio_include}" "") - -# use generic plugin maker for dlep-router -oonf_create_plugin("dlep_router" "${router_source}" "${router_include}" "") - -# combined radio/router plugin -oonf_create_plugin("dlep_proxy" "${proxy_source}" "${proxy_include}" "") +oonf_create_plugin("dlep" "${source}" "${include}" "") diff --git a/src/generic/dlep/README_DLEP_RADIO b/src/generic/dlep/README_DLEP_RADIO index 760e51d9..b6f1ae39 100644 --- a/src/generic/dlep/README_DLEP_RADIO +++ b/src/generic/dlep/README_DLEP_RADIO @@ -2,10 +2,10 @@ ================== DLEP_RADIO plugin by Henning Rogge -The plugin implements Draft-07 of the Dynamic Link Exchange Protocol of the +The plugin implements RFC 8175 (Dynamic Link Exchange Protocol) of the IETF MANET group. It was written as part of the EU CONFINE project in 2014. -See https://tools.ietf.org/html/draft-ietf-manet-dlep-07 for details. +See https://tools.ietf.org/html/rfc8175 for details. diff --git a/src/generic/dlep/README_DLEP_ROUTER b/src/generic/dlep/README_DLEP_ROUTER index 8ff6ec16..3a81390d 100644 --- a/src/generic/dlep/README_DLEP_ROUTER +++ b/src/generic/dlep/README_DLEP_ROUTER @@ -2,10 +2,10 @@ ================== DLEP_ROUTER plugin by Henning Rogge -The plugin implements Draft-07 of the Dynamic Link Exchange Protocol of the +The plugin implements RFC 8175 (Dynamic Link Exchange Protocol) of the IETF MANET group. It was written as part of the EU CONFINE project in 2014. -See https://tools.ietf.org/html/draft-ietf-manet-dlep-07 for details. +See https://tools.ietf.org/html/rfc8175 for details. diff --git a/src/generic/dlep/radio/dlep_radio.c b/src/generic/dlep/dlep.c similarity index 58% rename from src/generic/dlep/radio/dlep_radio.c rename to src/generic/dlep/dlep.c index 6cb3edf4..1cc5e23d 100644 --- a/src/generic/dlep/radio/dlep_radio.c +++ b/src/generic/dlep/dlep.c @@ -57,13 +57,25 @@ #include #include #include +#include #include - +#include + +#include +#include +#include +#include +#include +#include +#include +#include #include #include -#include #include -#include +#include +#include +#include +#include /* prototypes */ static void _early_cfg_init(void); @@ -71,7 +83,8 @@ static int _init(void); static void _cleanup(void); static void _initiate_shutdown(void); -static void _cb_config_changed(void); +static void _cb_radio_config_changed(void); +static void _cb_router_config_changed(void); /* configuration */ static const char *_UDP_MODE[] = { @@ -80,6 +93,53 @@ static const char *_UDP_MODE[] = { [DLEP_IF_UDP_ALWAYS] = DLEP_IF_UDP_ALWAYS_STR, }; +static struct cfg_schema_entry _router_entries[] = { + CFG_MAP_STRING(dlep_router_if, interf.session.cfg.peer_type, "peer_type", "OONF DLEP Router", + "Identification string of DLEP router endpoint"), + + CFG_MAP_NETADDR_V4(dlep_router_if, interf.udp_config.multicast_v4, "discovery_mc_v4", + DLEP_WELL_KNOWN_MULTICAST_ADDRESS, "IPv4 address to send discovery UDP packet to", false, false), + CFG_MAP_NETADDR_V6(dlep_router_if, interf.udp_config.multicast_v6, "discovery_mc_v6", + DLEP_WELL_KNOWN_MULTICAST_ADDRESS_6, "IPv6 address to send discovery UDP packet to", false, false), + CFG_MAP_INT32_MINMAX(dlep_router_if, interf.udp_config.multicast_port, "discovery_port", + DLEP_WELL_KNOWN_MULTICAST_PORT_TXT, "UDP port for discovery packets", 0, 1, 65535), + + CFG_MAP_ACL_V46(dlep_router_if, interf.udp_config.bindto, "discovery_bindto", "fe80::/64", + "Filter to determine the binding of the UDP discovery socket"), + + CFG_MAP_CLOCK_MIN(dlep_router_if, interf.session.cfg.discovery_interval, "discovery_interval", "1.000", + "Interval in seconds between two discovery beacons", 1000), + CFG_MAP_CLOCK_MINMAX(dlep_router_if, interf.session.cfg.heartbeat_interval, "heartbeat_interval", "1.000", + "Interval in seconds between two heartbeat signals", 1000, 65535000), + + CFG_MAP_CHOICE(dlep_router_if, interf.udp_mode, "udp_mode", DLEP_IF_UDP_SINGLE_SESSION_STR, + "Determines the UDP behavior of the router. 'none' never sends/processes UDP, 'single_session' only does" + " if no DLEP session is active and 'always' always sends/processes UDP and allows multiple sessions", + _UDP_MODE), + + CFG_MAP_STRING_ARRAY(dlep_router_if, interf.udp_config.interface, "datapath_if", "", + "Overwrite datapath interface for incoming dlep traffic, used for" + " receiving DLEP data through out-of-band channel.", + IF_NAMESIZE), + + CFG_MAP_NETADDR_V46(dlep_router_if, connect_to_addr, "connect_to", "-", + "IP to directly connect to a known DLEP radio TCP socket", false, true), + CFG_MAP_INT32_MINMAX(dlep_router_if, connect_to_port, "connect_to_port", DLEP_WELL_KNOWN_SESSION_PORT_TXT, + "TCP port to directly connect to a known DLEP radio TCP socket", 0, 1, 65535), +}; + +static struct cfg_schema_section _router_section = { + .type = "dlep_router", + .mode = CFG_SSMODE_NAMED, + + .help = "name of the layer2 interface DLEP router will put its data into", + + .cb_delta_handler = _cb_router_config_changed, + + .entries = _router_entries, + .entry_count = ARRAYSIZE(_router_entries), +}; + static struct cfg_schema_entry _radio_entries[] = { CFG_MAP_STRING_ARRAY(dlep_radio_if, interf.udp_config.interface, "datapath_if", "", "Name of interface to talk to dlep router (default is section name)", IF_NAMESIZE), @@ -119,15 +179,17 @@ static struct cfg_schema_entry _radio_entries[] = { }; static struct cfg_schema_section _radio_section = { - .type = OONF_DLEP_RADIO_SUBSYSTEM, + .type = "dlep_radio", .mode = CFG_SSMODE_NAMED, .help = "name of the layer2 interface DLEP radio will take its data from", - .cb_delta_handler = _cb_config_changed, + .cb_delta_handler = _cb_radio_config_changed, .entries = _radio_entries, .entry_count = ARRAYSIZE(_radio_entries), + + .next_section = &_router_section }; /* subsystem declaration */ @@ -136,13 +198,15 @@ static const char *_dependencies[] = { OONF_LAYER2_SUBSYSTEM, OONF_PACKET_SUBSYSTEM, OONF_STREAM_SUBSYSTEM, + OONF_TELNET_SUBSYSTEM, OONF_TIMER_SUBSYSTEM, + OONF_VIEWER_SUBSYSTEM, }; -static struct oonf_subsystem _dlep_radio_subsystem = { - .name = OONF_DLEP_RADIO_SUBSYSTEM, +static struct oonf_subsystem _dlep_subsystem = { + .name = OONF_DLEP_SUBSYSTEM, .dependencies = _dependencies, .dependencies_count = ARRAYSIZE(_dependencies), - .descr = "OONF DLEP radio plugin", + .descr = "OONF DLEP plugin", .author = "Henning Rogge", .cfg_section = &_radio_section, @@ -152,14 +216,18 @@ static struct oonf_subsystem _dlep_radio_subsystem = { .initiate_shutdown = _initiate_shutdown, .cleanup = _cleanup, }; -DECLARE_OONF_PLUGIN(_dlep_radio_subsystem); +DECLARE_OONF_PLUGIN(_dlep_subsystem); /* logging */ +enum oonf_log_source LOG_DLEP; enum oonf_log_source LOG_DLEP_RADIO; +enum oonf_log_source LOG_DLEP_ROUTER; static void _early_cfg_init(void) { - LOG_DLEP_RADIO = _dlep_radio_subsystem.logging; + LOG_DLEP = _dlep_subsystem.logging; + LOG_DLEP_RADIO = oonf_log_register_source("dlep_radio"); + LOG_DLEP_ROUTER = oonf_log_register_source("dlep_router"); } /** @@ -168,7 +236,19 @@ _early_cfg_init(void) { */ static int _init(void) { + dlep_extension_init(); + dlep_session_init(); + dlep_base_ip_init(); + dlep_base_metric_init(); + dlep_l1_statistics_init(); + dlep_l2_statistics_init(); + dlep_lid_init(); + dlep_dns_init(); + dlep_radio_attributes_init(); + dlep_radio_interface_init(); + dlep_router_interface_init(); + dlep_telnet_init(); return 0; } @@ -178,6 +258,7 @@ _init(void) { static void _initiate_shutdown(void) { dlep_radio_terminate_all_sessions(); + dlep_router_terminate_all_sessions(); } /** @@ -185,14 +266,60 @@ _initiate_shutdown(void) { */ static void _cleanup(void) { + dlep_telnet_cleanup(); dlep_radio_interface_cleanup(); + dlep_router_interface_cleanup(); + dlep_base_ip_cleanup(); + dlep_extension_cleanup(); } /** * Callback for configuration changes */ static void -_cb_config_changed(void) { +_cb_router_config_changed(void) { + struct dlep_router_if *interface; + const char *ifname; + char ifbuf[IF_NAMESIZE]; + + ifname = cfg_get_phy_if(ifbuf, _router_section.section_name); + + if (!_router_section.post) { + /* remove old session object */ + interface = dlep_router_get_by_layer2_if(ifname); + if (interface) { + dlep_router_remove_interface(interface); + } + return; + } + + /* get session object or create one */ + interface = dlep_router_add_interface(ifname); + if (!interface) { + return; + } + + /* read configuration */ + if (cfg_schema_tobin(interface, _router_section.post, _router_entries, ARRAYSIZE(_router_entries))) { + OONF_WARN(LOG_DLEP_ROUTER, "Could not convert dlep_radio config to bin"); + return; + } + + /* use section name as default for datapath interface */ + if (!interface->interf.udp_config.interface[0]) { + strscpy(interface->interf.udp_config.interface, _router_section.section_name, + sizeof(interface->interf.udp_config.interface)); + } + else { + cfg_get_phy_if(interface->interf.udp_config.interface, interface->interf.udp_config.interface); + } + + /* apply settings */ + dlep_router_apply_interface_settings(interface); +} + +static void +_cb_radio_config_changed(void) { struct dlep_radio_if *interface; const char *ifname; char ifbuf[IF_NAMESIZE]; @@ -218,7 +345,7 @@ _cb_config_changed(void) { /* read configuration */ error = cfg_schema_tobin(interface, _radio_section.post, _radio_entries, ARRAYSIZE(_radio_entries)); if (error) { - OONF_WARN(LOG_DLEP_RADIO, "Could not convert " OONF_DLEP_RADIO_SUBSYSTEM " config to bin (%d)", error); + OONF_WARN(LOG_DLEP_RADIO, "Could not convert dlep_router config to bin (%d)", error); return; } diff --git a/src/generic/dlep/dlep_session.c b/src/generic/dlep/dlep_session.c index 044dfa5d..d4cf8d25 100644 --- a/src/generic/dlep/dlep_session.c +++ b/src/generic/dlep/dlep_session.c @@ -48,6 +48,7 @@ #include #include #include +#include #include #include @@ -169,6 +170,7 @@ dlep_session_add(struct dlep_session *session, const char *l2_ifname, const stru } avl_init(&session->_ext_ip.prefix_modification, avl_comp_netaddr, false); + session->activation_time = oonf_clock_getNow(); OONF_INFO(session->log_source, "Add session on %s", session->l2_listener.name); return 0; @@ -189,6 +191,9 @@ dlep_session_remove(struct dlep_session *session) { OONF_DEBUG(session->log_source, "Remove session if %s to %s", session->l2_listener.name, netaddr_socket_to_string(&nbuf, &session->remote_socket)); + netaddr_socket_invalidate(&session->local_socket); + netaddr_socket_invalidate(&session->remote_socket); + os_interface_remove(&session->l2_listener); parser = &session->parser; diff --git a/src/generic/dlep/dlep_telnet.c b/src/generic/dlep/dlep_telnet.c new file mode 100644 index 00000000..904ee568 --- /dev/null +++ b/src/generic/dlep/dlep_telnet.c @@ -0,0 +1,229 @@ + +/* + * The olsr.org Optimized Link-State Routing daemon version 2 (olsrd2) + * Copyright (c) 2004-2015, the olsr.org team - see HISTORY file + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of olsr.org, olsrd nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * Visit http://www.olsr.org for more information. + * + * If you find this software useful feel free to make a donation + * to the project. For more information see the website or contact + * the copyright holders. + * + */ + +#include +#include +#include + +#include +#include +#include +#include +#include + +#define KEY_IF_NAME "if_name" +#define KEY_IF_SOCKET4 "if_socket4" +#define KEY_IF_SOCKET6 "if_socket6" +#define KEY_IF_RADIO "if_radio" +#define KEY_SESSION_LOCAL "session_local" +#define KEY_SESSION_REMOTE "session_remote" +#define KEY_SESSION_UPTIME "session_uptime" + +static enum oonf_telnet_result _cb_dlepinfo_cmd(struct oonf_telnet_data *con); +static enum oonf_telnet_result _cb_dlepinfo_help(struct oonf_telnet_data *con); + +static int _cb_create_text_if(struct oonf_viewer_template *); +static int _cb_create_text_session(struct oonf_viewer_template *); +static void _initialize_if_values(struct dlep_if *interf); +static void _initialize_session_values(struct dlep_session *session, bool raw); + +/* telnet interface variables */ +static char _value_if_name[IF_NAMESIZE]; +static struct netaddr_str _value_if_socket4; +static struct netaddr_str _value_if_socket6; +static char _value_if_radio[TEMPLATE_JSON_BOOL_LENGTH]; +static struct netaddr_str _value_session_local; +static struct netaddr_str _value_session_remote; +static struct isonumber_str _value_session_uptime; + +static struct abuf_template_data_entry _tde_if[] = { + { KEY_IF_NAME, _value_if_name, true }, + { KEY_IF_RADIO, _value_if_radio, true }, + { KEY_IF_SOCKET4, _value_if_socket4.buf, true }, + { KEY_IF_SOCKET6, _value_if_socket6.buf, true }, +}; +static struct abuf_template_data_entry _tde_session[] = { + { KEY_SESSION_LOCAL, _value_session_local.buf, true }, + { KEY_SESSION_REMOTE, _value_session_remote.buf, true }, + { KEY_SESSION_UPTIME, _value_session_uptime.buf, true }, +}; + +static struct abuf_template_data _td_if[] = { + { _tde_if, ARRAYSIZE(_tde_if) }, +}; +static struct abuf_template_data _td_session[] = { + { _tde_if, ARRAYSIZE(_tde_if) }, + { _tde_session, ARRAYSIZE(_tde_session) }, +}; + +static struct oonf_viewer_template _templates[] = { + { + .data = _td_if, + .data_size = ARRAYSIZE(_td_if), + .json_name = "interface", + .cb_function = _cb_create_text_if, + }, + { + .data = _td_session, + .data_size = ARRAYSIZE(_td_session), + .json_name = "session", + .cb_function = _cb_create_text_session, + } +}; + +static struct abuf_template_storage _template_storage; + +static struct oonf_telnet_command _dlep_cmd = + TELNET_CMD("dlepinfo", _cb_dlepinfo_cmd, "",.help_handler = _cb_dlepinfo_help); + +/** + * Initialize DLEP telnet interface + * @return always 0 + */ +int +dlep_telnet_init(void) { + oonf_telnet_add(&_dlep_cmd); + return 0; +} + +/** + * Cleanup DLEP telnet interface + */ +void +dlep_telnet_cleanup(void) { + oonf_telnet_remove(&_dlep_cmd); +} + +/** + * Callback for dlepinfo telnet command + * @param con telnet connection + * @return telnet result + */ +static enum oonf_telnet_result +_cb_dlepinfo_cmd(struct oonf_telnet_data *con) { + return oonf_viewer_telnet_handler( + con->out, &_template_storage, OONF_DLEP_SUBSYSTEM, con->parameter, _templates, ARRAYSIZE(_templates)); +} + +/** + * Callback for dlepinfo telnet help command + * @param con telnet connection + * @return telnet result + */ +static enum oonf_telnet_result +_cb_dlepinfo_help(struct oonf_telnet_data *con) { + return oonf_viewer_telnet_help( + con->out, OONF_DLEP_SUBSYSTEM, con->parameter, _templates, ARRAYSIZE(_templates)); +} + +/** + * Create the telnet output for 'dlepinfo interface' + * @param template viewer template + * @return always 0 + */ +static int +_cb_create_text_if(struct oonf_viewer_template *template) { + struct dlep_if *interf; + + avl_for_each_element(dlep_if_get_tree(true), interf, _node) { + _initialize_if_values(interf); + oonf_viewer_output_print_line(template); + } + avl_for_each_element(dlep_if_get_tree(false), interf, _node) { + _initialize_if_values(interf); + oonf_viewer_output_print_line(template); + } + return 0; +} + +/** + * Create the telnet output for 'dlepinfo session' + * @param template viewer template + * @return always 0 + */ +static int +_cb_create_text_session(struct oonf_viewer_template *template) { + struct dlep_if *interf; + struct dlep_session *session; + + avl_for_each_element(dlep_if_get_tree(true), interf, _node) { + _initialize_if_values(interf); + + avl_for_each_element(&interf->session_tree, session, _node) { + _initialize_session_values(session, template->create_raw); + oonf_viewer_output_print_line(template); + } + } + avl_for_each_element(dlep_if_get_tree(false), interf, _node) { + _initialize_if_values(interf); + + avl_for_each_element(&interf->session_tree, session, _node) { + _initialize_session_values(session, template->create_raw); + oonf_viewer_output_print_line(template); + } + } + return 0; +} + +/** + * Initialize the value buffers for a DLEP interface + * @param interf DLEP interface + */ +static void +_initialize_if_values(struct dlep_if *interf) { + strscpy(_value_if_name, interf->l2_ifname, sizeof(_value_if_name)); + netaddr_socket_to_string(&_value_if_socket4, &interf->udp.socket_v4.local_socket); + netaddr_socket_to_string(&_value_if_socket6, &interf->udp.socket_v6.local_socket); + strscpy(_value_if_radio, json_getbool(interf->radio), sizeof(_value_if_radio)); +} + +/** + * Initialize the value buffers for a DLEP session + * @param session DLEP session + * @param raw true to switch numeric values to raw mode, false for ISO prefix mode + */ +static void +_initialize_session_values(struct dlep_session *session, bool raw) { + netaddr_socket_to_string(&_value_session_local, &session->local_socket); + netaddr_socket_to_string(&_value_session_remote, &session->remote_socket); + oonf_clock_toIntervalString_ext(&_value_session_uptime, + -oonf_clock_get_relative(session->activation_time), raw); +} diff --git a/src/generic/dlep/ext_base_proto/proto_radio.c b/src/generic/dlep/ext_base_proto/proto_radio.c index e9d8644b..a57f84fb 100644 --- a/src/generic/dlep/ext_base_proto/proto_radio.c +++ b/src/generic/dlep/ext_base_proto/proto_radio.c @@ -535,7 +535,7 @@ _l2_neigh_added(struct oonf_layer2_neigh *l2neigh, struct oonf_layer2_destinatio return; } - avl_for_each_element(&radio_if->interf.session_tree, radio_session, _node) { + avl_for_each_element(&radio_if->interf.session_tree, radio_session, session._node) { if (l2dest && !radio_session->session.cfg.send_proxied) { continue; } @@ -564,7 +564,7 @@ _l2_neigh_changed( return; } - avl_for_each_element(&radio_if->interf.session_tree, radio_session, _node) { + avl_for_each_element(&radio_if->interf.session_tree, radio_session, session._node) { if (l2dest && !radio_session->session.cfg.send_proxied) { continue; } @@ -618,7 +618,7 @@ _l2_neigh_removed( return; } - avl_for_each_element(&radio_if->interf.session_tree, radio_session, _node) { + avl_for_each_element(&radio_if->interf.session_tree, radio_session, session._node) { if (l2dest && !radio_session->session.cfg.send_proxied) { continue; } @@ -654,7 +654,7 @@ _cb_l2_net_changed(void *ptr) { return; } - avl_for_each_element(&radio_if->interf.session_tree, radio_session, _node) { + avl_for_each_element(&radio_if->interf.session_tree, radio_session, session._node) { if (radio_session->session.restrict_signal == DLEP_ALL_SIGNALS) { dlep_session_generate_signal(&radio_session->session, DLEP_SESSION_UPDATE, NULL); } diff --git a/src/generic/dlep/radio/dlep_radio_interface.c b/src/generic/dlep/radio/dlep_radio_interface.c index 2c072cfe..b3ffed2e 100644 --- a/src/generic/dlep/radio/dlep_radio_interface.c +++ b/src/generic/dlep/radio/dlep_radio_interface.c @@ -57,7 +57,6 @@ #include #include -#include #include #include @@ -68,8 +67,9 @@ #include #include #include -#include #include +#include +#include static void _cleanup_interface(struct dlep_radio_if *interface); @@ -104,21 +104,9 @@ int dlep_radio_interface_init(void) { oonf_class_add(&_interface_class); - dlep_extension_init(); - dlep_session_init(); dlep_radio_session_init(); dlep_base_proto_radio_init(); - dlep_base_ip_init(); - dlep_base_metric_init(); - dlep_l1_statistics_init(); - dlep_l2_statistics_init(); - dlep_radio_attributes_init(); - dlep_lid_init(); - dlep_dns_init(); -#if 0 - oonf_layer2_origin_add(&_l2_origin); - oonf_layer2_origin_add(&_l2_default_origin); -#endif + _shutting_down = false; return 0; } @@ -137,7 +125,6 @@ dlep_radio_interface_cleanup(void) { oonf_class_remove(&_interface_class); dlep_radio_session_cleanup(); - dlep_extension_cleanup(); #if 0 oonf_layer2_origin_remove(&_l2_origin); oonf_layer2_origin_remove(&_l2_default_origin); @@ -275,7 +262,7 @@ dlep_radio_terminate_all_sessions(void) { _shutting_down = true; avl_for_each_element(dlep_if_get_tree(true), interf, interf._node) { - avl_for_each_element(&interf->interf.session_tree, radio_session, _node) { + avl_for_each_element(&interf->interf.session_tree, radio_session, session._node) { dlep_session_terminate(&radio_session->session, DLEP_STATUS_OKAY, "DLEP radio is shutting down"); } } @@ -290,7 +277,7 @@ _cleanup_interface(struct dlep_radio_if *interface) { struct dlep_radio_session *stream, *it; /* close TCP connection and socket */ - avl_for_each_element_safe(&interface->interf.session_tree, stream, _node, it) { + avl_for_each_element_safe(&interface->interf.session_tree, stream, session._node, it) { dlep_radio_remove_session(stream); } } diff --git a/src/generic/dlep/radio/dlep_radio_session.c b/src/generic/dlep/radio/dlep_radio_session.c index 682bc62b..6cc5bbce 100644 --- a/src/generic/dlep/radio/dlep_radio_session.c +++ b/src/generic/dlep/radio/dlep_radio_session.c @@ -56,10 +56,10 @@ #include #include #include -#include #include -#include #include +#include +#include static int _cb_incoming_tcp(struct oonf_stream_session *); static void _cb_tcp_lost(struct oonf_stream_session *); @@ -135,12 +135,15 @@ _cb_incoming_tcp(struct oonf_stream_session *tcp_session) { radio_session->session.cb_send_buffer = _cb_send_buffer; radio_session->session.cb_end_session = _cb_end_session; memcpy(&radio_session->session.cfg, &interface->interf.session.cfg, sizeof(radio_session->session.cfg)); - memcpy( - &radio_session->session.remote_socket, &tcp_session->remote_socket, sizeof(radio_session->session.remote_socket)); + + memcpy(&radio_session->session.local_socket, &tcp_session->stream_socket->local_socket, + sizeof(radio_session->session.local_socket)); + memcpy(&radio_session->session.remote_socket, &tcp_session->remote_socket, + sizeof(radio_session->session.remote_socket)); /* attach to session tree of interface */ - radio_session->_node.key = &radio_session->stream.remote_socket; - avl_insert(&interface->interf.session_tree, &radio_session->_node); + radio_session->session._node.key = &radio_session->stream.remote_socket; + avl_insert(&interface->interf.session_tree, &radio_session->session._node); /* copy socket information */ memcpy( @@ -182,7 +185,7 @@ _cb_tcp_lost(struct oonf_stream_session *tcp_session) { dlep_session_remove(&radio_session->session); /* remove from session tree of interface */ - avl_remove(&radio_session->interface->interf.session_tree, &radio_session->_node); + avl_remove(&radio_session->interface->interf.session_tree, &radio_session->session._node); } /** diff --git a/src/generic/dlep/router/dlep_router.c b/src/generic/dlep/router/dlep_router.c deleted file mode 100644 index 94fd1fd1..00000000 --- a/src/generic/dlep/router/dlep_router.c +++ /dev/null @@ -1,229 +0,0 @@ - -/* - * The olsr.org Optimized Link-State Routing daemon version 2 (olsrd2) - * Copyright (c) 2004-2015, the olsr.org team - see HISTORY file - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * * Neither the name of olsr.org, olsrd nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS - * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE - * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN - * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - * Visit http://www.olsr.org for more information. - * - * If you find this software useful feel free to make a donation - * to the project. For more information see the website or contact - * the copyright holders. - * - */ - -/** - * @file - */ - -#include -#include - -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -/* prototypes */ -static void _early_cfg_init(void); -static int _init(void); -static void _initiate_shutdown(void); -static void _cleanup(void); - -static void _cb_config_changed(void); - -/* configuration */ -static const char *_UDP_MODE[] = { - [DLEP_IF_UDP_NONE] = DLEP_IF_UDP_NONE_STR, - [DLEP_IF_UDP_SINGLE_SESSION] = DLEP_IF_UDP_SINGLE_SESSION_STR, - [DLEP_IF_UDP_ALWAYS] = DLEP_IF_UDP_ALWAYS_STR, -}; - -static struct cfg_schema_entry _router_entries[] = { - CFG_MAP_STRING(dlep_router_if, interf.session.cfg.peer_type, "peer_type", "OONF DLEP Router", - "Identification string of DLEP router endpoint"), - - CFG_MAP_NETADDR_V4(dlep_router_if, interf.udp_config.multicast_v4, "discovery_mc_v4", - DLEP_WELL_KNOWN_MULTICAST_ADDRESS, "IPv4 address to send discovery UDP packet to", false, false), - CFG_MAP_NETADDR_V6(dlep_router_if, interf.udp_config.multicast_v6, "discovery_mc_v6", - DLEP_WELL_KNOWN_MULTICAST_ADDRESS_6, "IPv6 address to send discovery UDP packet to", false, false), - CFG_MAP_INT32_MINMAX(dlep_router_if, interf.udp_config.multicast_port, "discovery_port", - DLEP_WELL_KNOWN_MULTICAST_PORT_TXT, "UDP port for discovery packets", 0, 1, 65535), - - CFG_MAP_ACL_V46(dlep_router_if, interf.udp_config.bindto, "discovery_bindto", "fe80::/64", - "Filter to determine the binding of the UDP discovery socket"), - - CFG_MAP_CLOCK_MIN(dlep_router_if, interf.session.cfg.discovery_interval, "discovery_interval", "1.000", - "Interval in seconds between two discovery beacons", 1000), - CFG_MAP_CLOCK_MINMAX(dlep_router_if, interf.session.cfg.heartbeat_interval, "heartbeat_interval", "1.000", - "Interval in seconds between two heartbeat signals", 1000, 65535000), - - CFG_MAP_CHOICE(dlep_router_if, interf.udp_mode, "udp_mode", DLEP_IF_UDP_SINGLE_SESSION_STR, - "Determines the UDP behavior of the router. 'none' never sends/processes UDP, 'single_session' only does" - " if no DLEP session is active and 'always' always sends/processes UDP and allows multiple sessions", - _UDP_MODE), - - CFG_MAP_STRING_ARRAY(dlep_router_if, interf.udp_config.interface, "datapath_if", "", - "Overwrite datapath interface for incoming dlep traffic, used for" - " receiving DLEP data through out-of-band channel.", - IF_NAMESIZE), - - CFG_MAP_NETADDR_V46(dlep_router_if, connect_to_addr, "connect_to", "-", - "IP to directly connect to a known DLEP radio TCP socket", false, true), - CFG_MAP_INT32_MINMAX(dlep_router_if, connect_to_port, "connect_to_port", DLEP_WELL_KNOWN_SESSION_PORT_TXT, - "TCP port to directly connect to a known DLEP radio TCP socket", 0, 1, 65535), -}; - -static struct cfg_schema_section _router_section = { - .type = OONF_DLEP_ROUTER_SUBSYSTEM, - .mode = CFG_SSMODE_NAMED, - - .help = "name of the layer2 interface DLEP router will put its data into", - - .cb_delta_handler = _cb_config_changed, - - .entries = _router_entries, - .entry_count = ARRAYSIZE(_router_entries), -}; - -/* plugin declaration */ -static const char *_dependencies[] = { - OONF_CLASS_SUBSYSTEM, - OONF_LAYER2_SUBSYSTEM, - OONF_PACKET_SUBSYSTEM, - OONF_STREAM_SUBSYSTEM, - OONF_TIMER_SUBSYSTEM, -}; -static struct oonf_subsystem _dlep_router_subsystem = { - .name = OONF_DLEP_ROUTER_SUBSYSTEM, - .dependencies = _dependencies, - .dependencies_count = ARRAYSIZE(_dependencies), - .descr = "OONF DLEP router plugin", - .author = "Henning Rogge", - - .cfg_section = &_router_section, - - .early_cfg_init = _early_cfg_init, - .init = _init, - .initiate_shutdown = _initiate_shutdown, - .cleanup = _cleanup, -}; -DECLARE_OONF_PLUGIN(_dlep_router_subsystem); - -enum oonf_log_source LOG_DLEP_ROUTER; - -static void -_early_cfg_init(void) { - LOG_DLEP_ROUTER = _dlep_router_subsystem.logging; -} - -/** - * Plugin constructor for dlep router - * @return -1 if an error happened, 0 otherwise - */ -static int -_init(void) { - dlep_router_interface_init(); - return 0; -} - -/** - * Send a clean Peer Terminate before we drop the session to shutdown - */ -static void -_initiate_shutdown(void) { - dlep_router_terminate_all_sessions(); -} - -/** - * Plugin destructor for dlep router - */ -static void -_cleanup(void) { - dlep_router_interface_cleanup(); -} - -/** - * Callback for configuration changes - */ -static void -_cb_config_changed(void) { - struct dlep_router_if *interface; - const char *ifname; - char ifbuf[IF_NAMESIZE]; - - ifname = cfg_get_phy_if(ifbuf, _router_section.section_name); - - if (!_router_section.post) { - /* remove old session object */ - interface = dlep_router_get_by_layer2_if(ifname); - if (interface) { - dlep_router_remove_interface(interface); - } - return; - } - - /* get session object or create one */ - interface = dlep_router_add_interface(ifname); - if (!interface) { - return; - } - - /* read configuration */ - if (cfg_schema_tobin(interface, _router_section.post, _router_entries, ARRAYSIZE(_router_entries))) { - OONF_WARN(LOG_DLEP_ROUTER, "Could not convert " OONF_DLEP_ROUTER_SUBSYSTEM " config to bin"); - return; - } - - /* use section name as default for datapath interface */ - if (!interface->interf.udp_config.interface[0]) { - strscpy(interface->interf.udp_config.interface, _router_section.section_name, - sizeof(interface->interf.udp_config.interface)); - } - else { - cfg_get_phy_if(interface->interf.udp_config.interface, interface->interf.udp_config.interface); - } - - /* apply settings */ - dlep_router_apply_interface_settings(interface); -} diff --git a/src/generic/dlep/router/dlep_router_interface.c b/src/generic/dlep/router/dlep_router_interface.c index 81bf08b9..f380eec8 100644 --- a/src/generic/dlep/router/dlep_router_interface.c +++ b/src/generic/dlep/router/dlep_router_interface.c @@ -63,8 +63,10 @@ #include #include -#include #include +#include +#include +#include #include #include @@ -74,8 +76,6 @@ #include #include #include -#include -#include static void _connect_to_setup(struct dlep_router_if *router_if); static void _check_connect_to(struct dlep_router_if *router_if); @@ -118,15 +118,8 @@ void dlep_router_interface_init(void) { oonf_class_add(&_router_if_class); - dlep_extension_init(); - dlep_session_init(); dlep_router_session_init(); dlep_base_proto_router_init(); - dlep_base_metric_init(); - dlep_base_ip_init(); - dlep_l1_statistics_init(); - dlep_l2_statistics_init(); - dlep_radio_attributes_init(); dlep_lid_init(); dlep_dns_init(); @@ -154,9 +147,7 @@ dlep_router_interface_cleanup(void) { oonf_class_remove(&_router_if_class); - dlep_base_ip_cleanup(); dlep_router_session_cleanup(); - dlep_extension_cleanup(); #if 0 oonf_layer2_origin_remove(&_l2_origin); oonf_layer2_origin_remove(&_l2_default_origin); @@ -294,7 +285,7 @@ dlep_router_terminate_all_sessions(void) { _shutting_down = true; avl_for_each_element(dlep_if_get_tree(false), interf, interf._node) { - avl_for_each_element(&interf->interf.session_tree, router_session, _node) { + avl_for_each_element(&interf->interf.session_tree, router_session, session._node) { dlep_session_terminate(&router_session->session, DLEP_STATUS_OKAY, "DLEP router is shutting down"); } } @@ -340,7 +331,7 @@ _cleanup_interface(struct dlep_router_if *interface) { struct dlep_router_session *stream, *it; /* close TCP connection and socket */ - avl_for_each_element_safe(&interface->interf.session_tree, stream, _node, it) { + avl_for_each_element_safe(&interface->interf.session_tree, stream, session._node, it) { dlep_router_remove_session(stream); } } diff --git a/src/generic/dlep/router/dlep_router_session.c b/src/generic/dlep/router/dlep_router_session.c index e51a2c59..14aba73c 100644 --- a/src/generic/dlep/router/dlep_router_session.c +++ b/src/generic/dlep/router/dlep_router_session.c @@ -59,10 +59,10 @@ #include #include #include -#include #include -#include #include +#include +#include static void _cb_socket_terminated(struct oonf_stream_socket *stream_socket); static void _cb_tcp_lost(struct oonf_stream_session *); @@ -102,7 +102,7 @@ struct dlep_router_session * dlep_router_get_session(struct dlep_router_if *interf, union netaddr_socket *remote) { struct dlep_router_session *session; - return avl_find_element(&interf->interf.session_tree, remote, session, _node); + return avl_find_element(&interf->interf.session_tree, remote, session, session._node); } /** @@ -135,7 +135,7 @@ dlep_router_add_session(struct dlep_router_if *interf, union netaddr_socket *loc /* initialize tree node */ memcpy(&router_session->session.remote_socket, remote, sizeof(*remote)); - router_session->_node.key = &router_session->session.remote_socket; + router_session->session._node.key = &router_session->session.remote_socket; /* configure and open TCP session */ router_session->tcp.config.session_timeout = 120000; /* 120 seconds */ @@ -177,8 +177,12 @@ dlep_router_add_session(struct dlep_router_if *interf, union netaddr_socket *loc /* initialize back pointer */ router_session->interface = interf; + /* copy local socket */ + memcpy(&router_session->session.local_socket, &router_session->stream->stream_socket->local_socket, + sizeof(router_session->session.local_socket)); + /* add session to interface */ - avl_insert(&interf->interf.session_tree, &router_session->_node); + avl_insert(&interf->interf.session_tree, &router_session->session._node); /* inform all extensions */ avl_for_each_element(dlep_extension_get_tree(), ext, _node) { @@ -241,8 +245,8 @@ _cb_tcp_lost(struct oonf_stream_session *tcp_session) { dlep_session_remove(&router_session->session); /* remove from session tree of interface */ - if (avl_is_node_added(&router_session->_node)) { - avl_remove(&router_session->interface->interf.session_tree, &router_session->_node); + if (avl_is_node_added(&router_session->session._node)) { + avl_remove(&router_session->interface->interf.session_tree, &router_session->session._node); } } From 7537bc3df77e66136dc8dab7927e2206fe301b55 Mon Sep 17 00:00:00 2001 From: Henning Rogge Date: Wed, 29 May 2019 14:34:36 +0200 Subject: [PATCH 24/63] Add support for sending prefixes from router to radio with session update --- apps/dlep-radio/CMakeLists.txt | 1 + include/oonf/generic/dlep/dlep.h | 6 + .../oonf/generic/dlep/ext_base_proto/proto.h | 3 + .../generic/dlep/router/dlep_router_session.h | 10 +- src/base/oonf_layer2.c | 7 +- src/generic/dlep/ext_base_ip/ip.c | 55 ++++- src/generic/dlep/ext_base_proto/proto.c | 97 +++++---- src/generic/dlep/ext_base_proto/proto_radio.c | 43 +--- .../dlep/ext_base_proto/proto_router.c | 47 +---- src/generic/dlep/radio/dlep_radio_interface.c | 20 +- src/generic/dlep/radio/dlep_radio_session.c | 2 +- .../dlep/router/dlep_router_interface.c | 27 +-- src/generic/dlep/router/dlep_router_session.c | 6 +- src/generic/layer2_export/layer2_export.c | 198 ++++++++++++++++-- 14 files changed, 318 insertions(+), 204 deletions(-) diff --git a/apps/dlep-radio/CMakeLists.txt b/apps/dlep-radio/CMakeLists.txt index 13fddf3b..016b6765 100644 --- a/apps/dlep-radio/CMakeLists.txt +++ b/apps/dlep-radio/CMakeLists.txt @@ -48,6 +48,7 @@ IF (NOT OONF_STATIC_PLUGINS) remotecontrol layer2_config layer2_import + layer2_export dlep ) ENDIF (NOT OONF_STATIC_PLUGINS) diff --git a/include/oonf/generic/dlep/dlep.h b/include/oonf/generic/dlep/dlep.h index d2807d76..83860a2c 100644 --- a/include/oonf/generic/dlep/dlep.h +++ b/include/oonf/generic/dlep/dlep.h @@ -49,5 +49,11 @@ /*! subsystem identifier */ #define OONF_DLEP_SUBSYSTEM "dlep" +/*! memory classes of DLEP */ +#define OONF_CLASS_DLEP_RADIO_INTERFACE "dlep radio interface" +#define OONF_CLASS_DLEP_RADIO_SESSION "dlep radio session" +#define OONF_CLASS_DLEP_ROUTER_INTERFACE "dlep router interface" +#define OONF_CLASS_DLEP_ROUTER_SESSION "dlep router session" + #endif /* DLEP_H_ */ diff --git a/include/oonf/generic/dlep/ext_base_proto/proto.h b/include/oonf/generic/dlep/ext_base_proto/proto.h index 732ccb06..17076155 100644 --- a/include/oonf/generic/dlep/ext_base_proto/proto.h +++ b/include/oonf/generic/dlep/ext_base_proto/proto.h @@ -65,4 +65,7 @@ int dlep_base_proto_process_heartbeat(struct dlep_extension *, struct dlep_sessi int dlep_base_proto_write_mac_only(struct dlep_extension *, struct dlep_session *session, const struct oonf_layer2_neigh_key *neigh); +int dlep_base_proto_process_session_update(struct dlep_extension *ext, struct dlep_session *session); +int dlep_base_proto_process_session_update_ack(struct dlep_extension *ext, struct dlep_session *session); + #endif /* _PROTO_H_ */ diff --git a/include/oonf/generic/dlep/router/dlep_router_session.h b/include/oonf/generic/dlep/router/dlep_router_session.h index feb83393..a8644e56 100644 --- a/include/oonf/generic/dlep/router/dlep_router_session.h +++ b/include/oonf/generic/dlep/router/dlep_router_session.h @@ -90,9 +90,17 @@ struct dlep_router_session { void dlep_router_session_init(void); void dlep_router_session_cleanup(void); -struct dlep_router_session *dlep_router_get_session(struct dlep_router_if *interf, union netaddr_socket *remote); +struct dlep_router_session *dlep_router_get_if_session(struct dlep_router_if *interf, union netaddr_socket *remote); struct dlep_router_session *dlep_router_add_session(struct dlep_router_if *interf, union netaddr_socket *local, union netaddr_socket *remote); void dlep_router_remove_session(struct dlep_router_session *); +static INLINE struct dlep_router_session * +dlep_router_get_session(struct dlep_session *session) { + if (session->radio) { + return NULL; + } + return container_of(session, struct dlep_router_session, session); +} + #endif /* DLEP_ROUTER_SESSION_H_ */ diff --git a/src/base/oonf_layer2.c b/src/base/oonf_layer2.c index e03151af..e003bf1b 100644 --- a/src/base/oonf_layer2.c +++ b/src/base/oonf_layer2.c @@ -771,10 +771,13 @@ oonf_layer2_net_add_ip( l2addr->_global_node.key = &l2addr->ip; avl_insert(&_local_peer_ips_tree, &l2addr->_global_node); + l2addr->origin = origin; + oonf_class_event(&_l2net_addr_class, l2addr, OONF_OBJECT_ADDED); } - - l2addr->origin = origin; + else { + l2addr->origin = origin; + } return l2addr; } diff --git a/src/generic/dlep/ext_base_ip/ip.c b/src/generic/dlep/ext_base_ip/ip.c index 1d2c0ae1..a45db090 100644 --- a/src/generic/dlep/ext_base_ip/ip.c +++ b/src/generic/dlep/ext_base_ip/ip.c @@ -56,6 +56,7 @@ #include #include #include +#include #include @@ -67,11 +68,11 @@ struct _prefix_storage { static void _cb_session_init(struct dlep_session *session); static void _cb_session_cleanup(struct dlep_session *session); -static int _radio_write_session_update( +static int _write_session_update( struct dlep_extension *ext, struct dlep_session *session, const struct oonf_layer2_neigh_key *neigh); static int _radio_write_destination_update( struct dlep_extension *ext, struct dlep_session *session, const struct oonf_layer2_neigh_key *neigh); -static enum dlep_parser_error _router_process_session_update(struct dlep_extension *ext, struct dlep_session *session); +static enum dlep_parser_error _process_session_update(struct dlep_extension *ext, struct dlep_session *session); static enum dlep_parser_error _router_process_destination_update( struct dlep_extension *ext, struct dlep_session *session); static void _add_prefix(struct avl_tree *tree, struct netaddr *addr); @@ -93,14 +94,27 @@ static const uint16_t _ip_duplicate_tlvs[] = { /* supported signals of this extension */ static struct dlep_extension_signal _signals[] = { + { + .id = DLEP_SESSION_INITIALIZATION, + .supported_tlvs = _ip_tlvs, + .supported_tlv_count = ARRAYSIZE(_ip_tlvs), + .duplicate_tlvs = _ip_duplicate_tlvs, + .duplicate_tlv_count = ARRAYSIZE(_ip_duplicate_tlvs), + .add_radio_tlvs = _write_session_update, + .process_radio = _process_session_update, + .add_router_tlvs = _write_session_update, + .process_router = _process_session_update, + }, { .id = DLEP_SESSION_INITIALIZATION_ACK, .supported_tlvs = _ip_tlvs, .supported_tlv_count = ARRAYSIZE(_ip_tlvs), .duplicate_tlvs = _ip_duplicate_tlvs, .duplicate_tlv_count = ARRAYSIZE(_ip_duplicate_tlvs), - .add_radio_tlvs = _radio_write_session_update, - .process_router = _router_process_session_update, + .add_radio_tlvs = _write_session_update, + .process_radio = _process_session_update, + .add_router_tlvs = _write_session_update, + .process_router = _process_session_update, }, { .id = DLEP_SESSION_UPDATE, @@ -108,8 +122,10 @@ static struct dlep_extension_signal _signals[] = { .supported_tlv_count = ARRAYSIZE(_ip_tlvs), .duplicate_tlvs = _ip_duplicate_tlvs, .duplicate_tlv_count = ARRAYSIZE(_ip_duplicate_tlvs), - .add_radio_tlvs = _radio_write_session_update, - .process_router = _router_process_session_update, + .add_radio_tlvs = _write_session_update, + .process_radio = _process_session_update, + .add_router_tlvs = _write_session_update, + .process_router = _process_session_update, }, { .id = DLEP_DESTINATION_UP, @@ -224,10 +240,11 @@ _handle_if_ip(struct dlep_session *session, struct netaddr *last_session_if_ip, } static int -_radio_write_session_update(struct dlep_extension *ext __attribute__((unused)), struct dlep_session *session, +_write_session_update(struct dlep_extension *ext __attribute__((unused)), struct dlep_session *session, const struct oonf_layer2_neigh_key *neigh __attribute__((unused))) { struct _prefix_storage *storage, *storage_it; struct dlep_radio_session *radio_session; + struct dlep_router_session *router_session; struct oonf_layer2_peer_address *peer_ip; struct oonf_layer2_net *l2net; struct os_interface *os_if; @@ -239,6 +256,10 @@ _radio_write_session_update(struct dlep_extension *ext __attribute__((unused)), /* announce newly added interface prefixes */ if (l2net) { avl_for_each_element(&l2net->local_peer_ips, peer_ip, _net_node) { + if (peer_ip->origin == session->l2_default_origin + || peer_ip->origin == session->l2_origin) { + continue; + } if (avl_find(&session->_ext_ip.prefix_modification, &peer_ip->ip)) { /* prefix already known to session */ continue; @@ -259,9 +280,14 @@ _radio_write_session_update(struct dlep_extension *ext __attribute__((unused)), /* remove missing interface prefixes */ avl_for_each_element_safe(&session->_ext_ip.prefix_modification, storage, _node, storage_it) { - if (l2net && avl_find(&l2net->local_peer_ips, &storage->prefix)) { - /* prefix is still on interface */ - continue; + if (l2net) { + peer_ip = oonf_layer2_net_get_local_ip(l2net, &storage->prefix); + if (peer_ip != NULL + && peer_ip->origin != session->l2_default_origin + && peer_ip->origin != session->l2_origin) { + /* prefix is still on interface */ + continue; + } } OONF_INFO(session->log_source, "Removed prefix '%s' for session update", @@ -284,6 +310,13 @@ _radio_write_session_update(struct dlep_extension *ext __attribute__((unused)), _handle_if_ip(session, &session->_ext_ip.if_ip_v4, os_if->if_linklocal_v4, os_if->if_v4); _handle_if_ip(session, &session->_ext_ip.if_ip_v6, os_if->if_linklocal_v6, os_if->if_v6); } + router_session = dlep_router_get_session(session); + if (router_session) { + os_if = router_session->interface->interf.udp._if_listener.data; + _handle_if_ip(session, &session->_ext_ip.if_ip_v4, os_if->if_linklocal_v4, os_if->if_v4); + _handle_if_ip(session, &session->_ext_ip.if_ip_v6, os_if->if_linklocal_v6, os_if->if_v6); + } + return 0; } @@ -372,7 +405,7 @@ _process_session_ip_tlvs( } static enum dlep_parser_error -_router_process_session_update(struct dlep_extension *ext __attribute((unused)), struct dlep_session *session) { +_process_session_update(struct dlep_extension *ext __attribute((unused)), struct dlep_session *session) { struct oonf_layer2_net *l2net; struct netaddr ip; struct dlep_parser_value *value; diff --git a/src/generic/dlep/ext_base_proto/proto.c b/src/generic/dlep/ext_base_proto/proto.c index edf8d344..8d591111 100644 --- a/src/generic/dlep/ext_base_proto/proto.c +++ b/src/generic/dlep/ext_base_proto/proto.c @@ -93,12 +93,10 @@ static const uint16_t _session_initack_mandatory[] = { /* peer update */ static const uint16_t _peer_update_tlvs[] = { - DLEP_IPV4_ADDRESS_TLV, - DLEP_IPV6_ADDRESS_TLV, + DLEP_STATUS_TLV, }; static const uint16_t _peer_update_duplicates[] = { - DLEP_IPV4_ADDRESS_TLV, - DLEP_IPV6_ADDRESS_TLV, + DLEP_STATUS_TLV, }; /* peer update ack */ @@ -122,20 +120,10 @@ static const uint16_t _peer_terminationack_tlvs[] = { /* destination up */ static const uint16_t _dst_up_tlvs[] = { DLEP_MAC_ADDRESS_TLV, - DLEP_IPV4_ADDRESS_TLV, - DLEP_IPV6_ADDRESS_TLV, - DLEP_IPV4_SUBNET_TLV, - DLEP_IPV6_SUBNET_TLV, }; static const uint16_t _dst_up_mandatory[] = { DLEP_MAC_ADDRESS_TLV, }; -static const uint16_t _dst_up_duplicates[] = { - DLEP_IPV4_ADDRESS_TLV, - DLEP_IPV6_ADDRESS_TLV, - DLEP_IPV4_SUBNET_TLV, - DLEP_IPV6_SUBNET_TLV, -}; /* destination up ack */ static const uint16_t _dst_up_ack_tlvs[] = { @@ -168,27 +156,14 @@ static const uint16_t _dst_down_ack_mandatory[] = { /* destination update */ static const uint16_t _dst_update_tlvs[] = { DLEP_MAC_ADDRESS_TLV, - DLEP_IPV4_ADDRESS_TLV, - DLEP_IPV6_ADDRESS_TLV, - DLEP_IPV4_SUBNET_TLV, - DLEP_IPV6_SUBNET_TLV, }; static const uint16_t _dst_update_mandatory[] = { DLEP_MAC_ADDRESS_TLV, }; -static const uint16_t _dst_update_duplicates[] = { - DLEP_IPV4_ADDRESS_TLV, - DLEP_IPV6_ADDRESS_TLV, - DLEP_IPV4_SUBNET_TLV, - DLEP_IPV6_SUBNET_TLV, -}; /* link characteristics request */ static const uint16_t _linkchar_req_tlvs[] = { DLEP_MAC_ADDRESS_TLV, - DLEP_CDRR_TLV, - DLEP_CDRT_TLV, - DLEP_LATENCY_TLV, }; static const uint16_t _linkchar_req_mandatory[] = { DLEP_MAC_ADDRESS_TLV, @@ -197,14 +172,6 @@ static const uint16_t _linkchar_req_mandatory[] = { /* link characteristics ack */ static const uint16_t _linkchar_ack_tlvs[] = { DLEP_MAC_ADDRESS_TLV, - DLEP_MDRR_TLV, - DLEP_MDRT_TLV, - DLEP_CDRR_TLV, - DLEP_CDRT_TLV, - DLEP_LATENCY_TLV, - DLEP_RESOURCES_TLV, - DLEP_RLQR_TLV, - DLEP_RLQT_TLV, DLEP_STATUS_TLV, }; static const uint16_t _linkchar_ack_mandatory[] = { @@ -265,8 +232,6 @@ static struct dlep_extension_signal _signals[] = { .supported_tlv_count = ARRAYSIZE(_dst_up_tlvs), .mandatory_tlvs = _dst_up_mandatory, .mandatory_tlv_count = ARRAYSIZE(_dst_up_mandatory), - .duplicate_tlvs = _dst_up_duplicates, - .duplicate_tlv_count = ARRAYSIZE(_dst_up_duplicates), }, { .id = DLEP_DESTINATION_UP_ACK, @@ -295,8 +260,6 @@ static struct dlep_extension_signal _signals[] = { .supported_tlv_count = ARRAYSIZE(_dst_update_tlvs), .mandatory_tlvs = _dst_update_mandatory, .mandatory_tlv_count = ARRAYSIZE(_dst_update_mandatory), - .duplicate_tlvs = _dst_update_duplicates, - .duplicate_tlv_count = ARRAYSIZE(_dst_update_duplicates), }, { .id = DLEP_HEARTBEAT, @@ -563,6 +526,62 @@ dlep_base_proto_write_mac_only( return 0; } +/** + * Process the peer update message + * @param ext (this) dlep extension + * @param session dlep session + * @return -1 if an error happened, 0 otherwise + */ +int +dlep_base_proto_process_session_update(struct dlep_extension *ext __attribute__((unused)), struct dlep_session *session) { + struct oonf_layer2_net *l2net; + int result; + + l2net = oonf_layer2_net_add(session->l2_listener.name); + if (!l2net) { + return DLEP_NEW_PARSER_OUT_OF_MEMORY; + } + + if (!session->radio) { + /* metric data is only transported radio to router */ + result = dlep_reader_map_l2neigh_data(l2net->neighdata, session, &_base_proto); + if (result) { + OONF_INFO(session->log_source, "tlv mapping failed for extension %u: %u", ext->id, result); + return DLEP_NEW_PARSER_INTERNAL_ERROR; + } + } + + /* generate ACK */ + if (dlep_session_generate_signal_status(session, DLEP_SESSION_UPDATE_ACK, NULL, DLEP_STATUS_OKAY, "Success")) { + return DLEP_NEW_PARSER_INTERNAL_ERROR; + } + dlep_base_proto_print_status(session); + return DLEP_NEW_PARSER_OKAY; +} + +/** + * Process the peer update ack message + * @param ext (this) dlep extension + * @param session dlep session + * @return always 0 + */ +int +dlep_base_proto_process_session_update_ack(struct dlep_extension *ext __attribute__((unused)), struct dlep_session *session) { + dlep_base_proto_print_status(session); + if (session->_peer_state == DLEP_PEER_SEND_UPDATE) { + if (dlep_session_generate_signal(session, DLEP_SESSION_UPDATE, NULL)) { + // TODO: do we need to terminate here? + return DLEP_NEW_PARSER_INTERNAL_ERROR; + } + session->_peer_state = DLEP_PEER_WAIT_FOR_UPDATE_ACK; + } + else { + session->_peer_state = DLEP_PEER_IDLE; + } + dlep_base_proto_print_status(session); + return DLEP_NEW_PARSER_OKAY; +} + /** * Callback triggered when to generate a new heartbeat * @param ptr timer instance that fired diff --git a/src/generic/dlep/ext_base_proto/proto_radio.c b/src/generic/dlep/ext_base_proto/proto_radio.c index a57f84fb..bd32258f 100644 --- a/src/generic/dlep/ext_base_proto/proto_radio.c +++ b/src/generic/dlep/ext_base_proto/proto_radio.c @@ -67,8 +67,6 @@ static void _cb_cleanup_radio(struct dlep_session *); static enum dlep_parser_error _radio_process_peer_discovery(struct dlep_extension *, struct dlep_session *); static enum dlep_parser_error _radio_process_session_init(struct dlep_extension *, struct dlep_session *); -static enum dlep_parser_error _radio_process_session_update(struct dlep_extension *, struct dlep_session *); -static enum dlep_parser_error _radio_process_session_update_ack(struct dlep_extension *, struct dlep_session *); static enum dlep_parser_error _radio_process_destination_up_ack(struct dlep_extension *, struct dlep_session *); static enum dlep_parser_error _radio_process_destination_down_ack(struct dlep_extension *, struct dlep_session *); static enum dlep_parser_error _radio_process_link_char_request(struct dlep_extension *, struct dlep_session *); @@ -108,11 +106,11 @@ static struct dlep_extension_implementation _radio_signals[] = { }, { .id = DLEP_SESSION_UPDATE, - .process = _radio_process_session_update, + .process = dlep_base_proto_process_session_update, }, { .id = DLEP_SESSION_UPDATE_ACK, - .process = _radio_process_session_update_ack, + .process = dlep_base_proto_process_session_update_ack, }, { .id = DLEP_SESSION_TERMINATION, @@ -323,43 +321,6 @@ _radio_process_session_init(struct dlep_extension *ext __attribute__((unused)), return DLEP_NEW_PARSER_OKAY; } -/** - * Process the peer update message - * @param ext (this) dlep extension - * @param session dlep session - * @return -1 if an error happened, 0 otherwise - */ -static enum dlep_parser_error -_radio_process_session_update(struct dlep_extension *ext __attribute__((unused)), struct dlep_session *session) { - /* we don't support IP address exchange with the router at the moment */ - if (dlep_session_generate_signal_status(session, DLEP_SESSION_UPDATE_ACK, NULL, DLEP_STATUS_OKAY, "Success")) { - return DLEP_NEW_PARSER_INTERNAL_ERROR; - } - return DLEP_NEW_PARSER_OKAY; -} - -/** - * Process the peer update ack message - * @param ext (this) dlep extension - * @param session dlep session - * @return always 0 - */ -static int -_radio_process_session_update_ack(struct dlep_extension *ext __attribute__((unused)), struct dlep_session *session) { - dlep_base_proto_print_status(session); - if (session->_peer_state == DLEP_PEER_SEND_UPDATE) { - if (dlep_session_generate_signal(session, DLEP_SESSION_UPDATE, NULL)) { - // TODO: do we need to terminate here? - return DLEP_NEW_PARSER_INTERNAL_ERROR; - } - session->_peer_state = DLEP_PEER_WAIT_FOR_UPDATE_ACK; - } - else { - session->_peer_state = DLEP_PEER_IDLE; - } - return DLEP_NEW_PARSER_OKAY; -} - /** * Process the destination up ack message * @param ext (this) dlep extension diff --git a/src/generic/dlep/ext_base_proto/proto_router.c b/src/generic/dlep/ext_base_proto/proto_router.c index d048d0f9..a958debe 100644 --- a/src/generic/dlep/ext_base_proto/proto_router.c +++ b/src/generic/dlep/ext_base_proto/proto_router.c @@ -65,8 +65,6 @@ static void _cb_create_peer_discovery(struct oonf_timer_instance *); static enum dlep_parser_error _router_process_peer_offer(struct dlep_extension *, struct dlep_session *); static enum dlep_parser_error _router_process_session_init_ack(struct dlep_extension *, struct dlep_session *); -static enum dlep_parser_error _router_process_session_update(struct dlep_extension *, struct dlep_session *); -static enum dlep_parser_error _router_process_session_update_ack(struct dlep_extension *, struct dlep_session *); static enum dlep_parser_error _router_process_destination_up(struct dlep_extension *, struct dlep_session *); static enum dlep_parser_error _router_process_destination_up_ack(struct dlep_extension *, struct dlep_session *); static enum dlep_parser_error _router_process_destination_down(struct dlep_extension *, struct dlep_session *); @@ -96,11 +94,11 @@ static struct dlep_extension_implementation _router_signals[] = { }, { .id = DLEP_SESSION_UPDATE, - .process = _router_process_session_update, + .process = dlep_base_proto_process_session_update, }, { .id = DLEP_SESSION_UPDATE_ACK, - .process = _router_process_session_update_ack, + .process = dlep_base_proto_process_session_update_ack, }, { .id = DLEP_SESSION_TERMINATION, @@ -405,47 +403,6 @@ _router_process_session_init_ack(struct dlep_extension *ext __attribute__((unuse return DLEP_NEW_PARSER_OKAY; } -/** - * Process the peer update message - * @param ext (this) dlep extension - * @param session dlep session - * @return -1 if an error happened, 0 otherwise - */ -static int -_router_process_session_update(struct dlep_extension *ext __attribute__((unused)), struct dlep_session *session) { - struct oonf_layer2_net *l2net; - int result; - - l2net = oonf_layer2_net_add(session->l2_listener.name); - if (!l2net) { - return DLEP_NEW_PARSER_OUT_OF_MEMORY; - } - - result = dlep_reader_map_l2neigh_data(l2net->neighdata, session, _base); - if (result) { - OONF_INFO(session->log_source, "tlv mapping failed for extension %u: %u", ext->id, result); - return DLEP_NEW_PARSER_INTERNAL_ERROR; - } - - /* generate ACK */ - if (dlep_session_generate_signal_status(session, DLEP_SESSION_UPDATE_ACK, NULL, DLEP_STATUS_OKAY, "Success")) { - return DLEP_NEW_PARSER_INTERNAL_ERROR; - } - return DLEP_NEW_PARSER_OKAY; -} - -/** - * Process the peer update ack message - * @param ext (this) dlep extension - * @param session dlep session - * @return -1 if an error happened, 0 otherwise - */ -static enum dlep_parser_error -_router_process_session_update_ack(struct dlep_extension *ext __attribute__((unused)), struct dlep_session *session) { - dlep_base_proto_print_status(session); - return DLEP_NEW_PARSER_OKAY; -} - /** * Process the destination up message * @param ext (this) dlep extension diff --git a/src/generic/dlep/radio/dlep_radio_interface.c b/src/generic/dlep/radio/dlep_radio_interface.c index b3ffed2e..5e65a972 100644 --- a/src/generic/dlep/radio/dlep_radio_interface.c +++ b/src/generic/dlep/radio/dlep_radio_interface.c @@ -75,26 +75,12 @@ static void _cleanup_interface(struct dlep_radio_if *interface); /* DLEP interfaces */ static struct oonf_class _interface_class = { - .name = "DLEP radio interface", + .name = OONF_CLASS_DLEP_RADIO_INTERFACE, .size = sizeof(struct dlep_radio_if), }; static bool _shutting_down; -#if 0 -static struct oonf_layer2_origin _l2_origin = { - .name = "dlep_radio", - .proactive = true, - .priority = OONF_LAYER2_ORIGIN_RELIABLE, -}; - -static struct oonf_layer2_origin _l2_default_origin = { - .name = "dlep_radio_defaults", - .proactive = false, - .priority = OONF_LAYER2_ORIGIN_DEFAULT, -}; -#endif - /** * Initialize everything for dlep radio interfaces. This function also * initializes the dlep sessions. @@ -125,10 +111,6 @@ dlep_radio_interface_cleanup(void) { oonf_class_remove(&_interface_class); dlep_radio_session_cleanup(); -#if 0 - oonf_layer2_origin_remove(&_l2_origin); - oonf_layer2_origin_remove(&_l2_default_origin); -#endif } /** diff --git a/src/generic/dlep/radio/dlep_radio_session.c b/src/generic/dlep/radio/dlep_radio_session.c index 6cc5bbce..fdba395e 100644 --- a/src/generic/dlep/radio/dlep_radio_session.c +++ b/src/generic/dlep/radio/dlep_radio_session.c @@ -68,7 +68,7 @@ static void _cb_send_buffer(struct dlep_session *session, int af_family); static void _cb_end_session(struct dlep_session *session); static struct oonf_class _radio_session_class = { - .name = "DLEP TCP session", + .name = OONF_CLASS_DLEP_RADIO_SESSION, .size = sizeof(struct dlep_radio_session), }; diff --git a/src/generic/dlep/router/dlep_router_interface.c b/src/generic/dlep/router/dlep_router_interface.c index f380eec8..0db22734 100644 --- a/src/generic/dlep/router/dlep_router_interface.c +++ b/src/generic/dlep/router/dlep_router_interface.c @@ -84,26 +84,12 @@ static int _connect_to_if_changed(struct os_interface_listener *); static void _cb_check_connect_to_status(struct oonf_timer_instance *); static struct oonf_class _router_if_class = { - .name = "DLEP router interface", + .name = OONF_CLASS_DLEP_ROUTER_INTERFACE, .size = sizeof(struct dlep_router_if), }; static bool _shutting_down; -#if 0 -static struct oonf_layer2_origin _l2_origin = { - .name = "dlep_router", - .proactive = true, - .priority = OONF_LAYER2_ORIGIN_RELIABLE, -}; - -static struct oonf_layer2_origin _l2_default_origin = { - .name = "dlep_router_defaults", - .proactive = false, - .priority = OONF_LAYER2_ORIGIN_UNRELIABLE, -}; -#endif - static struct oonf_timer_class _connect_to_watchdog_class = { .name = "connect_to watchdog", .callback = _cb_check_connect_to_status, @@ -125,11 +111,6 @@ dlep_router_interface_init(void) { _shutting_down = false; -#if 0 - oonf_layer2_origin_add(&_l2_origin); - oonf_layer2_origin_add(&_l2_default_origin); -#endif - oonf_timer_add(&_connect_to_watchdog_class); } @@ -148,10 +129,6 @@ dlep_router_interface_cleanup(void) { oonf_class_remove(&_router_if_class); dlep_router_session_cleanup(); -#if 0 - oonf_layer2_origin_remove(&_l2_origin); - oonf_layer2_origin_remove(&_l2_default_origin); -#endif oonf_timer_remove(&_connect_to_watchdog_class); } @@ -349,7 +326,7 @@ _check_connect_to(struct dlep_router_if *router_if) { return; } - connect_to_session = dlep_router_get_session(router_if, &router_if->connect_to); + connect_to_session = dlep_router_get_if_session(router_if, &router_if->connect_to); if (connect_to_session != NULL && (connect_to_session->session._peer_state == DLEP_PEER_NOT_CONNECTED || connect_to_session->session._peer_state == DLEP_PEER_TERMINATED)) { diff --git a/src/generic/dlep/router/dlep_router_session.c b/src/generic/dlep/router/dlep_router_session.c index 14aba73c..a85c2788 100644 --- a/src/generic/dlep/router/dlep_router_session.c +++ b/src/generic/dlep/router/dlep_router_session.c @@ -72,7 +72,7 @@ static void _cb_end_session(struct dlep_session *session); /* session objects */ static struct oonf_class _router_session_class = { - .name = "DLEP router stream", + .name = OONF_CLASS_DLEP_ROUTER_SESSION, .size = sizeof(struct dlep_router_session), }; @@ -99,7 +99,7 @@ dlep_router_session_cleanup(void) { * @return dlep router session, NULL if not found */ struct dlep_router_session * -dlep_router_get_session(struct dlep_router_if *interf, union netaddr_socket *remote) { +dlep_router_get_if_session(struct dlep_router_if *interf, union netaddr_socket *remote) { struct dlep_router_session *session; return avl_find_element(&interf->interf.session_tree, remote, session, session._node); @@ -118,7 +118,7 @@ dlep_router_add_session(struct dlep_router_if *interf, union netaddr_socket *loc struct dlep_extension *ext; struct netaddr_str nbuf1, nbuf2; - router_session = dlep_router_get_session(interf, remote); + router_session = dlep_router_get_if_session(interf, remote); if (router_session) { OONF_DEBUG(LOG_DLEP_ROUTER, "use existing instance on" diff --git a/src/generic/layer2_export/layer2_export.c b/src/generic/layer2_export/layer2_export.c index e434d039..333c8f43 100644 --- a/src/generic/layer2_export/layer2_export.c +++ b/src/generic/layer2_export/layer2_export.c @@ -82,6 +82,9 @@ struct _l2export_data { /*! fib protocol */ int32_t fib_protocol; + /*! bind route to fixed interface */ + char fixed_if[IF_NAMESIZE]; + /*! tree of routes imported by this section */ struct avl_tree route_tree; @@ -129,7 +132,7 @@ static void _initiate_shutdown(void); static struct _l2export_data *_get_l2export(const char *name); static void _destroy_l2export(struct _l2export_data *); -static bool _is_matching_route(struct oonf_layer2_neighbor_address *, struct _l2export_data *); +static bool _is_matching_route(const struct netaddr *, const char *origin, struct _l2export_data *); static struct _l2export_route *_get_route(struct _l2export_data *data, struct os_route_key *key); static void _destroy_route(struct _l2export_route *route); @@ -138,6 +141,9 @@ static void _cb_route_finished(struct os_route *route, int error); static void _cb_l2neigh_ip_added(void *); static void _cb_l2neigh_ip_removed(void *); +static void _cb_l2net_ip_added(void *); +static void _cb_l2net_ip_removed(void *); + static void _cb_cfg_changed(void); static struct cfg_schema_entry _l2export_entries[] = { @@ -154,6 +160,8 @@ static struct cfg_schema_entry _l2export_entries[] = { "fib table for exported layer2 entries", 0, 1, 65535), CFG_MAP_INT32_MINMAX(_l2export_data, fib_protocol, "fib_protocol", "100", "fib protocol for exported layer2 entries", 0, 1, 255), + CFG_MAP_STRING_ARRAY(_l2export_data, fixed_if, "fixed_if", "", + "Set interface to bind all routes to", IF_NAMESIZE), }; static struct cfg_schema_section _l2export_section = { @@ -204,6 +212,14 @@ static struct oonf_class_extension _l2neighip_ext = { .cb_remove = _cb_l2neigh_ip_removed, }; +static struct oonf_class_extension _l2net_ext = { + .ext_name = "l2export listener", + .class_name = LAYER2_CLASS_NETWORK_ADDRESS, + + .cb_add = _cb_l2net_ip_added, + .cb_remove = _cb_l2net_ip_removed, +}; + /* tree of routing exporters */ static struct avl_tree _l2export_tree; @@ -219,6 +235,10 @@ _init(void) { if (oonf_class_extension_add(&_l2neighip_ext)) { return -1; } + if (oonf_class_extension_add(&_l2net_ext)) { + oonf_class_extension_remove(&_l2neighip_ext); + return -1; + } avl_init(&_l2export_tree, avl_comp_strcasecmp, false); avl_init(&_removal_tree, os_routing_avl_cmp_route_key, false); oonf_class_add(&_l2export_class); @@ -232,7 +252,9 @@ _init(void) { static void _cleanup(void) { oonf_class_remove(&_l2export_class); + oonf_class_remove(&_route_class); oonf_class_extension_remove(&_l2neighip_ext); + oonf_class_extension_remove(&_l2net_ext); } /** @@ -299,37 +321,39 @@ _destroy_l2export(struct _l2export_data *l2export) { } /** -* Checks if the originator name of a l2 neighbor address matches a pattern +* Checks if the a ip/originator pair matches the export filter +* @param ip ip address +* @param origin originator name * @param addr l2 neighbor address * @param pattern pattern (can end with an asterix wildcard) * @return true if matching, false otherwise */ static bool -_is_matching_route(struct oonf_layer2_neighbor_address *addr, struct _l2export_data *data) { +_is_matching_route(const struct netaddr *ip, const char *origin, struct _l2export_data *data) { int len; #ifdef OONF_LOG_DEBUG_INFO struct netaddr_str nbuf; #endif /* check prefix length */ - if (data->min_prefix_length > netaddr_get_prefix_length(&addr->ip)) { + if (data->min_prefix_length > netaddr_get_prefix_length(ip)) { OONF_DEBUG(LOG_L2EXPORT, "prefix length %u is too small (filter was %d)", - netaddr_get_prefix_length(&addr->ip), data->min_prefix_length); + netaddr_get_prefix_length(ip), data->min_prefix_length); return false; } - if (data->max_prefix_length < netaddr_get_prefix_length(&addr->ip)) { + if (data->max_prefix_length < netaddr_get_prefix_length(ip)) { OONF_DEBUG(LOG_L2EXPORT, "prefix length %u is too large (filter was %d)", - netaddr_get_prefix_length(&addr->ip), data->max_prefix_length); + netaddr_get_prefix_length(ip), data->max_prefix_length); return false; } /* check if destination matches */ - if (!netaddr_acl_check_accept(&data->filter, &addr->ip)) { - OONF_DEBUG(LOG_L2EXPORT, "Bad prefix %s", netaddr_to_string(&nbuf, &addr->ip)); + if (!netaddr_acl_check_accept(&data->filter, ip)) { + OONF_DEBUG(LOG_L2EXPORT, "Bad prefix %s", netaddr_to_string(&nbuf, ip)); return false; } /* check if originator name is exact match */ - if (strcmp(addr->origin->name, data->originator) == 0) { + if (strcmp(origin, data->originator) == 0) { return true; } @@ -339,7 +363,7 @@ _is_matching_route(struct oonf_layer2_neighbor_address *addr, struct _l2export_d return false; } - return strncmp(addr->origin->name, data->originator, len-1) == 0; + return strncmp(origin, data->originator, len-1) == 0; } /** @@ -460,8 +484,8 @@ _cb_l2neigh_ip_added(void *ptr) { struct _l2export_route *l2route; struct os_route_key rt_key; int8_t af; -#ifdef OONF_LOG_DEBUG_INFO struct os_route_str rbuf; +#ifdef OONF_LOG_DEBUG_INFO struct netaddr_str nbuf; #endif os_routing_init_sourcespec_prefix(&rt_key, &nip->ip); @@ -469,7 +493,7 @@ _cb_l2neigh_ip_added(void *ptr) { avl_for_each_element(&_l2export_tree, l2export, _node) { OONF_DEBUG(LOG_L2EXPORT, "Check export %s against originator %s", l2export->originator, nip->origin->name); - if (_is_matching_route(nip, l2export)) { + if (_is_matching_route(&nip->ip, nip->origin->name, l2export)) { OONF_DEBUG(LOG_L2EXPORT, "match"); l2route = _get_route(l2export, &rt_key); if (!l2route) { @@ -486,13 +510,25 @@ _cb_l2neigh_ip_added(void *ptr) { memcpy(&l2route->os.p.gw, oonf_layer2_neigh_get_nexthop(nip->l2neigh, af), sizeof(struct netaddr)); l2route->os.p.type = OS_ROUTE_UNICAST; l2route->os.p.metric = l2export->fib_distance; - l2route->os.p.if_index = nip->l2neigh->network->if_listener.data->index; + l2route->os.p.protocol = l2export->fib_protocol; l2route->os.p.table = l2export->fib_table; - OONF_DEBUG(LOG_L2EXPORT, "Add route %s to fib (gw was %s)", + /* get interface index */ + l2route->os.p.if_index = if_nametoindex(l2export->fixed_if); + if (!l2route->os.p.if_index) { + l2route->os.p.if_index = nip->l2neigh->network->if_listener.data->index; + } + if (!l2route->os.p.if_index) { + OONF_WARN(LOG_L2EXPORT, "Could not find interface for route %s", + os_routing_to_string(&rbuf, &l2route->os.p)); + continue; + } + + OONF_DEBUG(LOG_L2EXPORT, "Add route %s to fib (gw was %s, ifindex was %u)", os_routing_to_string(&rbuf, &l2route->os.p), - netaddr_to_string(&nbuf, oonf_layer2_neigh_get_nexthop(nip->l2neigh, af))); + netaddr_to_string(&nbuf, oonf_layer2_neigh_get_nexthop(nip->l2neigh, af)), + l2route->os.p.if_index); if (!os_routing_set(&l2route->os, true, true)) { l2route->status = ROUTE_ADDING; } @@ -516,7 +552,135 @@ _cb_l2neigh_ip_removed(void *ptr) { avl_for_each_element(&_l2export_tree, l2export, _node) { OONF_DEBUG(LOG_L2EXPORT, "Check export %s against originator %s", l2export->originator, nip->origin->name); - if (_is_matching_route(nip, l2export)) { + if (_is_matching_route(&nip->ip, nip->origin->name, l2export)) { + OONF_DEBUG(LOG_L2EXPORT, "match"); + l2route = avl_find_element(&l2export->route_tree, &rt_key, l2route, _node); + if (l2route) { + OONF_DEBUG(LOG_L2EXPORT, "found entry"); + _destroy_route(l2route); + } + } + } +} + +/** +* Callback triggered when a l2 network address is addrd +* @param ptr address being added +*/ +static void +_cb_l2net_ip_added(void *ptr) { + struct oonf_layer2_peer_address *pip, *pip2; + struct _l2export_data *l2export; + struct _l2export_route *l2route; + struct netaddr next_v4, next_v6; + const struct netaddr *next; + struct os_route_key rt_key; + int8_t af; + struct os_route_str rbuf; +#ifdef OONF_LOG_DEBUG_INFO + struct netaddr_str nbuf; +#endif + pip = ptr; + os_routing_init_sourcespec_prefix(&rt_key, &pip->ip); + + netaddr_invalidate(&next_v4); + netaddr_invalidate(&next_v6); + + avl_for_each_element(&pip->l2net->local_peer_ips, pip2, _net_node) { + if (netaddr_get_prefix_length(&pip2->ip) == netaddr_get_maxprefix(&pip2->ip)) { + switch (netaddr_get_address_family(&pip2->ip)) { + case AF_INET: + memcpy(&next_v4, &pip2->ip, sizeof(next_v4)); + break; + case AF_INET6: + memcpy(&next_v6, &pip2->ip, sizeof(next_v6)); + break; + default: + break; + } + } + } + + avl_for_each_element(&_l2export_tree, l2export, _node) { + OONF_DEBUG(LOG_L2EXPORT, "Check export %s against originator %s", + l2export->originator, pip->origin->name); + if (_is_matching_route(&pip->ip, pip->origin->name, l2export)) { + OONF_DEBUG(LOG_L2EXPORT, "match"); + l2route = _get_route(l2export, &rt_key); + if (!l2route) { + continue; + } + + OONF_DEBUG(LOG_L2EXPORT, "got entry"); + + // TODO: what if this route is not in state "nothing" ? + af = netaddr_get_address_family(&pip->ip); + + /* getnext hop */ + switch (af) { + case AF_INET: + next = &next_v4; + break; + case AF_INET6: + next = &next_v6; + break; + default: + next = &NETADDR_UNSPEC; + break; + } + if (netaddr_is_unspec(next)) { + /* no next hop */ + continue; + } + + /* set route parameters */ + l2route->os.p.family = af; + + memcpy(&l2route->os.p.gw, next, sizeof(struct netaddr)); + l2route->os.p.type = OS_ROUTE_UNICAST; + l2route->os.p.metric = l2export->fib_distance; + l2route->os.p.protocol = l2export->fib_protocol; + l2route->os.p.table = l2export->fib_table; + + /* get interface index */ + l2route->os.p.if_index = if_nametoindex(l2export->fixed_if); + if (!l2route->os.p.if_index) { + l2route->os.p.if_index = pip->l2net->if_listener.data->index; + } + if (!l2route->os.p.if_index) { + OONF_WARN(LOG_L2EXPORT, "Could not find interface for route %s", + os_routing_to_string(&rbuf, &l2route->os.p)); + continue; + } + + OONF_DEBUG(LOG_L2EXPORT, "Add route %s to fib (gw was %s, ifindex was %u)", + os_routing_to_string(&rbuf, &l2route->os.p), + netaddr_to_string(&nbuf, next), + l2route->os.p.if_index); + if (!os_routing_set(&l2route->os, true, true)) { + l2route->status = ROUTE_ADDING; + } + } + } +} + +/** +* Callback triggered when a l2 network address is removed +* @param ptr address being removed +*/ +static void +_cb_l2net_ip_removed(void *ptr) { + struct oonf_layer2_neighbor_address *nip = ptr; + struct _l2export_data *l2export; + struct _l2export_route *l2route; + struct os_route_key rt_key; + + os_routing_init_sourcespec_prefix(&rt_key, &nip->ip); + + avl_for_each_element(&_l2export_tree, l2export, _node) { + OONF_DEBUG(LOG_L2EXPORT, "Check export %s against originator %s", + l2export->originator, nip->origin->name); + if (_is_matching_route(&nip->ip, nip->origin->name, l2export)) { OONF_DEBUG(LOG_L2EXPORT, "match"); l2route = avl_find_element(&l2export->route_tree, &rt_key, l2route, _node); if (l2route) { From 79647dbecaf516f0b38b3b877272328d75720115 Mon Sep 17 00:00:00 2001 From: Henning Rogge Date: Mon, 3 Jun 2019 14:47:03 +0200 Subject: [PATCH 25/63] Fix gcc 8.3 release build --- src/generic/dlep/dlep_reader.c | 1 + src/generic/dns_query/dns.c | 1 + 2 files changed, 2 insertions(+) diff --git a/src/generic/dlep/dlep_reader.c b/src/generic/dlep/dlep_reader.c index 7752ce5c..cbc75e84 100644 --- a/src/generic/dlep/dlep_reader.c +++ b/src/generic/dlep/dlep_reader.c @@ -591,6 +591,7 @@ dlep_reader_map_identity(struct oonf_layer2_data *data, const struct oonf_layer2 } dlepvalue = dlep_parser_get_tlv_binary(&session->parser, value); + l2value = 0; if (meta->type == OONF_LAYER2_INTEGER_DATA || meta->type == OONF_LAYER2_BOOLEAN_DATA) { switch (value->length) { diff --git a/src/generic/dns_query/dns.c b/src/generic/dns_query/dns.c index 91e483c0..3ed66a83 100644 --- a/src/generic/dns_query/dns.c +++ b/src/generic/dns_query/dns.c @@ -30,6 +30,7 @@ #pragma GCC diagnostic ignored "-Wimplicit-fallthrough" #pragma GCC diagnostic ignored "-Wstrict-prototypes" #pragma GCC diagnostic ignored "-Wjump-misses-init" +#pragma GCC diagnostic ignored "-Warray-bounds" #if !defined(__FreeBSD__) && !defined(__sun) #ifndef _XOPEN_SOURCE From 97d75c454bba5b7276ef98381a8ea056b6209730 Mon Sep 17 00:00:00 2001 From: Henning Rogge Date: Thu, 6 Jun 2019 15:14:51 +0200 Subject: [PATCH 26/63] Continue with next filter-set when one filter of layer2import filter function fails. --- src/generic/layer2_import/layer2_import.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/generic/layer2_import/layer2_import.c b/src/generic/layer2_import/layer2_import.c index 6d50b42f..b9255f2e 100644 --- a/src/generic/layer2_import/layer2_import.c +++ b/src/generic/layer2_import/layer2_import.c @@ -408,7 +408,7 @@ _cb_rt_event(const struct os_route *route, bool set) { if (import->rttype != route->p.type) { OONF_DEBUG(LOG_L2_IMPORT, "Bad routing type %u (filter was %d)", route->p.type, import->rttype); - return; + continue; } /* check prefix length */ @@ -477,7 +477,7 @@ _cb_rt_event(const struct os_route *route, bool set) { } if (!l2net) { OONF_DEBUG(LOG_L2_IMPORT, "No l2 network '%s' found", l2ifname); - return; + continue; } mac = NULL; @@ -497,7 +497,7 @@ _cb_rt_event(const struct os_route *route, bool set) { if (!oonf_timer_is_active(&_route_reload_instance)) { oonf_timer_set(&_route_reload_instance, 1000); } - return; + continue; } dst = &route->p.key.dst; @@ -517,7 +517,7 @@ _cb_rt_event(const struct os_route *route, bool set) { l2neigh = oonf_layer2_neigh_add_lid(l2net, &nb_key); if (!l2neigh) { OONF_DEBUG(LOG_L2_IMPORT, "No l2 neighbor found"); - return; + continue; } OONF_DEBUG(LOG_L2_IMPORT, "Import layer2 neighbor..."); @@ -545,7 +545,6 @@ _cb_rt_event(const struct os_route *route, bool set) { static struct _import_entry * _get_import(const char *name) { struct _import_entry *import; - import = avl_find_element(&_import_tree, name, import, _node); if (import) { return import; From d38891324e37fd3043e0bd4d14ddc944764ecba0 Mon Sep 17 00:00:00 2001 From: Henning Rogge Date: Fri, 7 Jun 2019 14:58:31 +0200 Subject: [PATCH 27/63] Forced trigger of metric recalculation listeners --- include/oonf/nhdp/nhdp/nhdp_domain.h | 2 +- src/nhdp/nhdp/nhdp_db.c | 2 +- src/nhdp/nhdp/nhdp_domain.c | 15 ++++++++++----- src/nhdp/nhdp/nhdp_reader.c | 2 +- 4 files changed, 13 insertions(+), 8 deletions(-) diff --git a/include/oonf/nhdp/nhdp/nhdp_domain.h b/include/oonf/nhdp/nhdp/nhdp_domain.h index 082b4cf9..1e0f8285 100644 --- a/include/oonf/nhdp/nhdp/nhdp_domain.h +++ b/include/oonf/nhdp/nhdp/nhdp_domain.h @@ -341,7 +341,7 @@ EXPORT size_t nhdp_domain_encode_willingness_tlvvalue(uint8_t *tlvvalue, size_t EXPORT bool nhdp_domain_set_incoming_metric( struct nhdp_domain_metric *metric, struct nhdp_link *lnk, uint32_t metric_in); -EXPORT bool nhdp_domain_recalculate_metrics(struct nhdp_domain *domain, struct nhdp_neighbor *neigh); +EXPORT bool nhdp_domain_recalculate_metrics(struct nhdp_domain *domain, struct nhdp_neighbor *neigh, bool force); EXPORT enum nhdp_metric_result nhdp_domain_get_metric(struct nhdp_domain *domain, uint32_t *metric, struct oonf_layer2_neigh *neigh); EXPORT bool nhdp_domain_node_is_mpr(void); diff --git a/src/nhdp/nhdp/nhdp_db.c b/src/nhdp/nhdp/nhdp_db.c index 97ee027e..ace84dee 100644 --- a/src/nhdp/nhdp/nhdp_db.c +++ b/src/nhdp/nhdp/nhdp_db.c @@ -848,7 +848,7 @@ nhdp_db_link_update_status(struct nhdp_link *lnk) { if (old_status != lnk->status) { /* link status was changed */ lnk->last_status_change = oonf_clock_getNow(); - nhdp_domain_recalculate_metrics(NULL, lnk->neigh); + nhdp_domain_recalculate_metrics(NULL, lnk->neigh, true); nhdp_domain_delayed_mpr_recalculation(NULL, lnk->neigh); /* trigger change event */ diff --git a/src/nhdp/nhdp/nhdp_domain.c b/src/nhdp/nhdp/nhdp_domain.c index 4984d7b4..44e3b6a9 100644 --- a/src/nhdp/nhdp/nhdp_domain.c +++ b/src/nhdp/nhdp/nhdp_domain.c @@ -69,6 +69,7 @@ static void _remove_mpr(struct nhdp_domain *); static void _cb_update_everyone_routing_mpr(struct nhdp_domain *domain); static void _cb_update_everyone_flooding_mpr(struct nhdp_domain *domain); +static bool _recalculate_metrics(struct nhdp_domain *domain, struct nhdp_neighbor *neigh, bool trigger, bool force); static bool _recalculate_neighbor_metric(struct nhdp_domain *domain, struct nhdp_neighbor *neigh); static bool _recalculate_routing_mpr_set(struct nhdp_domain *domain); static bool _recalculate_flooding_mpr_set(void); @@ -485,10 +486,13 @@ nhdp_domain_process_metric_2hoptlv(struct nhdp_domain *domain, struct nhdp_l2hop /** * This will trigger a metric recalculation * @param domain NHDP domain of metric change, NULL for all domains + * @param trigger true to trigger listener, false otherwise + * @param force true to trigger a metric change regardless of values, false to + * check for change * return true if metric changed, false otherwise */ static bool -_recalculate_metrics(struct nhdp_domain *domain, struct nhdp_neighbor *neigh, bool trigger) { +_recalculate_metrics(struct nhdp_domain *domain, struct nhdp_neighbor *neigh, bool trigger, bool force) { struct nhdp_domain_listener *listener; bool changed_metric; @@ -500,7 +504,7 @@ _recalculate_metrics(struct nhdp_domain *domain, struct nhdp_neighbor *neigh, bo if (!domain) { list_for_each_element(&_domain_list, domain, _node) { - changed_metric |= _recalculate_metrics(domain, neigh, false); + changed_metric |= _recalculate_metrics(domain, neigh, false, force); } domain = NULL; } @@ -513,7 +517,7 @@ _recalculate_metrics(struct nhdp_domain *domain, struct nhdp_neighbor *neigh, bo changed_metric |= _recalculate_neighbor_metric(domain, neigh); } - if (trigger && changed_metric) { + if (trigger && (changed_metric || force)) { list_for_each_element(&_domain_listener_list, listener, _node) { /* trigger domain listeners */ if (listener->metric_update) { @@ -530,8 +534,9 @@ _recalculate_metrics(struct nhdp_domain *domain, struct nhdp_neighbor *neigh, bo } bool -nhdp_domain_recalculate_metrics(struct nhdp_domain *domain, struct nhdp_neighbor *neigh) { - return _recalculate_metrics(domain, neigh, true); +nhdp_domain_recalculate_metrics(struct nhdp_domain *domain, struct nhdp_neighbor *neigh, + bool force) { + return _recalculate_metrics(domain, neigh, true, force); } static void diff --git a/src/nhdp/nhdp/nhdp_reader.c b/src/nhdp/nhdp/nhdp_reader.c index ad918036..23450e78 100644 --- a/src/nhdp/nhdp/nhdp_reader.c +++ b/src/nhdp/nhdp/nhdp_reader.c @@ -983,7 +983,7 @@ _cb_msg_pass2_end(struct rfc5444_reader_tlvblock_context *context, bool dropped) nhdp_db_link_update_status(_current.link); /* update link metrics and MPR */ - nhdp_domain_recalculate_metrics(NULL, _current.neighbor); + nhdp_domain_recalculate_metrics(NULL, _current.neighbor, false); nhdp_domain_delayed_mpr_recalculation(NULL, _current.neighbor); return RFC5444_OKAY; From 63d36f02b1983dd44c2f03f3d3c2a361daf745bf Mon Sep 17 00:00:00 2001 From: Henning Rogge Date: Mon, 1 Jul 2019 14:35:58 +0200 Subject: [PATCH 28/63] Fix netlink multicast definition system --- include/oonf/base/os_linux/os_system_linux_data.h | 12 +++++++++--- src/base/os_linux/os_interface_linux.c | 12 ++++++++---- src/base/os_linux/os_routing_linux.c | 9 ++++++--- src/base/os_linux/os_system_linux.c | 10 +++++----- 4 files changed, 28 insertions(+), 15 deletions(-) diff --git a/include/oonf/base/os_linux/os_system_linux_data.h b/include/oonf/base/os_linux/os_system_linux_data.h index 06471c4d..812a4e7f 100644 --- a/include/oonf/base/os_linux/os_system_linux_data.h +++ b/include/oonf/base/os_linux/os_system_linux_data.h @@ -127,10 +127,16 @@ struct os_system_netlink { const char *name; /* list of multicast groups this handler requires */ - const uint32_t *multicast; - + const uint32_t *multicast_groups; + + /*! number of multicast groups this handler requires */ + size_t multicast_group_count; + + /* list of multicast groups this handler requires */ + const uint32_t *multicast_messages; + /*! number of multicast groups this handler requires */ - size_t multicast_count; + size_t multicast_message_count; /* reference to os system netlink multiplexer */ struct os_system_netlink_socket *nl_socket; diff --git a/src/base/os_linux/os_interface_linux.c b/src/base/os_linux/os_interface_linux.c index 48e24ce3..bade2e52 100644 --- a/src/base/os_linux/os_interface_linux.c +++ b/src/base/os_linux/os_interface_linux.c @@ -155,13 +155,17 @@ static struct oonf_subsystem _oonf_os_interface_subsystem = { }; DECLARE_OONF_PLUGIN(_oonf_os_interface_subsystem); -static const uint32_t _rtnetlink_mcast[] = { RTNLGRP_LINK, RTNLGRP_IPV4_IFADDR, RTNLGRP_IPV6_IFADDR }; +static const uint32_t _rtnetlink_mcast_groups[] = { RTNLGRP_LINK, RTNLGRP_IPV4_IFADDR, RTNLGRP_IPV6_IFADDR }; +static const uint32_t _rtnetlink_mcast_messages[] = { RTM_NEWLINK, RTM_NEWADDR, RTM_DELLINK, RTM_DELADDR }; static struct os_system_netlink _rtnetlink_handler = { .name = "interface", - .multicast = _rtnetlink_mcast, - .multicast_count = ARRAYSIZE(_rtnetlink_mcast), - + + .multicast_groups = _rtnetlink_mcast_groups, + .multicast_group_count = ARRAYSIZE(_rtnetlink_mcast_groups), + .multicast_messages = _rtnetlink_mcast_messages, + .multicast_message_count = ARRAYSIZE(_rtnetlink_mcast_messages), + .used_by = &_oonf_os_interface_subsystem, .cb_response = _cb_rtnetlink_response, .cb_multicast = _cb_rtnetlink_multicast, diff --git a/src/base/os_linux/os_routing_linux.c b/src/base/os_linux/os_routing_linux.c index 0a5ddaa4..18de9c8e 100644 --- a/src/base/os_linux/os_routing_linux.c +++ b/src/base/os_linux/os_routing_linux.c @@ -109,14 +109,17 @@ static struct route_type_translation _type_translation[] = { { OS_ROUTE_UNICAST, { OS_ROUTE_BLACKHOLE, RTN_BLACKHOLE }, { OS_ROUTE_NAT, RTN_NAT } }; /* netlink socket for route set/get commands */ -static const uint32_t _rtnetlink_mcast[] = { RTNLGRP_IPV4_ROUTE, RTNLGRP_IPV6_ROUTE }; +static const uint32_t _rtnetlink_mcast_groups[] = { RTNLGRP_IPV4_ROUTE, RTNLGRP_IPV6_ROUTE }; +static const uint32_t _rtnetlink_mcast_messages[] = { RTM_NEWROUTE, RTM_DELROUTE }; static struct os_system_netlink _rtnetlink_handler = { .name = "routing send", .used_by = &_oonf_os_routing_subsystem, - .multicast = &_rtnetlink_mcast[0], - .multicast_count = ARRAYSIZE(_rtnetlink_mcast), + .multicast_groups = &_rtnetlink_mcast_groups[0], + .multicast_group_count = ARRAYSIZE(_rtnetlink_mcast_groups), + .multicast_messages = &_rtnetlink_mcast_messages[0], + .multicast_message_count = ARRAYSIZE(_rtnetlink_mcast_messages), .cb_response = _cb_rtnetlink_response, .cb_multicast = _cb_rtnetlink_multicast, diff --git a/src/base/os_linux/os_system_linux.c b/src/base/os_linux/os_system_linux.c index 203fb757..65997c07 100644 --- a/src/base/os_linux/os_system_linux.c +++ b/src/base/os_linux/os_system_linux.c @@ -312,11 +312,11 @@ os_system_linux_netlink_add(struct os_system_netlink *nl, int protocol) { return -1; } - for (i = 0; i < nl->multicast_count; i++) { + for (i = 0; i < nl->multicast_group_count; i++) { if (setsockopt(os_fd_get_fd(&nl->nl_socket->nl_socket.fd), - SOL_NETLINK, NETLINK_ADD_MEMBERSHIP, &nl->multicast[i], sizeof(nl->multicast[i]))) { + SOL_NETLINK, NETLINK_ADD_MEMBERSHIP, &nl->multicast_groups[i], sizeof(nl->multicast_groups[i]))) { OONF_WARN(nl->used_by->logging, "Netlink '%s': could not join mc group: %d", - nl->name, nl->multicast[i]); + nl->name, nl->multicast_groups[i]); return -1; } } @@ -792,8 +792,8 @@ _netlink_handler(struct oonf_socket_entry *entry) { else { /* this seems to be multicast */ list_for_each_element(&nl_socket->handlers, nl_handler, _node) { - for (i=0; imulticast_count; i++) { - if (nl_handler->multicast[i] == nh->nlmsg_type) { + for (i=0; imulticast_message_count; i++) { + if (nl_handler->multicast_messages[i] == nh->nlmsg_type) { nl_handler->cb_multicast(nl_handler, nh); break; } From 6a478c77c56d82ee41457bed7f8314cc139904cf Mon Sep 17 00:00:00 2001 From: Henning Rogge Date: Fri, 12 Jul 2019 07:05:02 +0200 Subject: [PATCH 29/63] fix raw json output for timer entries --- include/oonf/base/oonf_timer.h | 17 +++++++++++++++-- src/nhdp/nhdpinfo/nhdpinfo.c | 24 ++++++++++++------------ src/olsrv2/olsrv2info/olsrv2info.c | 20 ++++++++++---------- 3 files changed, 37 insertions(+), 24 deletions(-) diff --git a/include/oonf/base/oonf_timer.h b/include/oonf/base/oonf_timer.h index 3bf2c139..77aae02c 100644 --- a/include/oonf/base/oonf_timer.h +++ b/include/oonf/base/oonf_timer.h @@ -168,19 +168,32 @@ oonf_timer_get_due(const struct oonf_timer_instance *timer) { * or "-1" if it has already fired. * @param buf target buffer * @param timer timer + * @param raw true to get raw time values, false to use ISO prefixes * @return pointer to string representation */ static INLINE const char * -oonf_timer_to_string(struct isonumber_str *buf, struct oonf_timer_instance *timer) { +oonf_timer_to_string_ext(struct isonumber_str *buf, struct oonf_timer_instance *timer, bool raw) { static const char NONE[] = "-1"; if (!oonf_timer_is_active(timer)) { return NONE; } else { - return oonf_clock_toIntervalString(buf, oonf_timer_get_due(timer)); + return oonf_clock_toIntervalString_ext(buf, oonf_timer_get_due(timer), raw); } } +/** + * Puts the number of seconds until the timer fires into the output buffer, + * or "-1" if it has already fired. + * @param buf target buffer + * @param timer timer + * @return pointer to string representation + */ +static INLINE const char * +oonf_timer_to_string(struct isonumber_str *buf, struct oonf_timer_instance *timer) { + return oonf_timer_to_string_ext(buf, timer, false); +} + /** * This is the one stop shop for all sort of timer manipulation. * Depending on the passed in parameters a new timer is started, diff --git a/src/nhdp/nhdpinfo/nhdpinfo.c b/src/nhdp/nhdpinfo/nhdpinfo.c index f57cec20..61f59126 100644 --- a/src/nhdp/nhdpinfo/nhdpinfo.c +++ b/src/nhdp/nhdpinfo/nhdpinfo.c @@ -71,7 +71,7 @@ static enum oonf_telnet_result _cb_nhdpinfo_help(struct oonf_telnet_data *con); static void _initialize_interface_values(struct nhdp_interface *nhdp_if); static void _initialize_interface_address_values(struct nhdp_interface_addr *if_addr); -static void _initialize_nhdp_link_values(struct nhdp_link *lnk); +static void _initialize_nhdp_link_values(struct nhdp_link *lnk, bool raw); static void _initialize_nhdp_domain_metric_values(struct nhdp_domain *domain, struct nhdp_metric *metric); static void _initialize_nhdp_neighbor_mpr_values( struct nhdp_domain *domain, struct nhdp_neighbor_domaindata *domaindata); @@ -309,8 +309,8 @@ static struct abuf_template_data_entry _tde_link_key[] = { static struct abuf_template_data_entry _tde_link[] = { { KEY_LINK_BINDTO, _value_link_bindto.buf, true }, - { KEY_LINK_VTIME_VALUE, _value_link_vtime_value.buf, false }, - { KEY_LINK_ITIME_VALUE, _value_link_itime_value.buf, false }, + { KEY_LINK_VTIME_VALUE, _value_link_vtime_value.buf, true }, + { KEY_LINK_ITIME_VALUE, _value_link_itime_value.buf, true }, { KEY_LINK_SYMTIME, _value_link_symtime.buf, false }, { KEY_LINK_HEARDTIME, _value_link_heardtime.buf, false }, { KEY_LINK_VTIME, _value_link_vtime.buf, false }, @@ -572,15 +572,15 @@ _initialize_interface_address_values(struct nhdp_interface_addr *if_addr) { * @param lnk NHDP link */ static void -_initialize_nhdp_link_values(struct nhdp_link *lnk) { +_initialize_nhdp_link_values(struct nhdp_link *lnk, bool raw) { netaddr_to_string(&_value_link_bindto, &lnk->if_addr); - oonf_clock_toIntervalString(&_value_link_vtime_value, lnk->vtime_value); - oonf_clock_toIntervalString(&_value_link_itime_value, lnk->itime_value); + oonf_clock_toIntervalString_ext(&_value_link_vtime_value, lnk->vtime_value, raw); + oonf_clock_toIntervalString_ext(&_value_link_itime_value, lnk->itime_value, raw); - oonf_timer_to_string(&_value_link_symtime, &lnk->sym_time); - oonf_timer_to_string(&_value_link_heardtime, &lnk->heard_time); - oonf_timer_to_string(&_value_link_vtime, &lnk->vtime); + oonf_timer_to_string_ext(&_value_link_symtime, &lnk->sym_time, raw); + oonf_timer_to_string_ext(&_value_link_heardtime, &lnk->heard_time, raw); + oonf_timer_to_string_ext(&_value_link_vtime, &lnk->vtime, raw); strscpy(_value_link_status, nhdp_db_link_status_to_string(lnk), sizeof(_value_link_status)); @@ -750,7 +750,7 @@ _cb_create_text_link(struct oonf_viewer_template *template) { _initialize_interface_values(nhdp_if); list_for_each_element(&nhdp_if->_links, nlink, _if_node) { - _initialize_nhdp_link_values(nlink); + _initialize_nhdp_link_values(nlink, template->create_raw); _initialize_nhdp_neighbor_values(nlink->neigh); list_for_each_element(nhdp_domain_get_list(), domain, _node) { @@ -781,7 +781,7 @@ _cb_create_text_link_address(struct oonf_viewer_template *template) { _initialize_interface_values(nhdpif); list_for_each_element(&nhdpif->_links, nhdplink, _if_node) { - _initialize_nhdp_link_values(nhdplink); + _initialize_nhdp_link_values(nhdplink, template->create_raw); _initialize_nhdp_neighbor_values(nhdplink->neigh); avl_for_each_element(&nhdplink->_addresses, laddr, _link_node) { @@ -812,7 +812,7 @@ _cb_create_text_link_twohop(struct oonf_viewer_template *template) { _initialize_interface_values(nhdpif); list_for_each_element(&nhdpif->_links, nhdplink, _if_node) { - _initialize_nhdp_link_values(nhdplink); + _initialize_nhdp_link_values(nhdplink, template->create_raw); _initialize_nhdp_neighbor_values(nhdplink->neigh); avl_for_each_element(&nhdplink->_2hop, twohop, _link_node) { diff --git a/src/olsrv2/olsrv2info/olsrv2info.c b/src/olsrv2/olsrv2info/olsrv2info.c index ad41817c..e9f34c4f 100644 --- a/src/olsrv2/olsrv2info/olsrv2info.c +++ b/src/olsrv2/olsrv2info/olsrv2info.c @@ -74,12 +74,12 @@ static enum oonf_telnet_result _cb_olsrv2info(struct oonf_telnet_data *con); static enum oonf_telnet_result _cb_olsrv2info_help(struct oonf_telnet_data *con); static void _initialize_originator_values(int af_type); -static void _initialize_old_originator_values(struct olsrv2_originator_set_entry *); +static void _initialize_old_originator_values(struct olsrv2_originator_set_entry *, bool raw); static void _initialize_domain_values(struct nhdp_domain *domain); static void _initialize_domain_link_metric_values(struct nhdp_domain *domain, uint32_t); static void _initialize_domain_distance(uint8_t); static void _initialize_lan_values(struct olsrv2_lan_entry *); -static void _initialize_node_values(struct olsrv2_tc_node *); +static void _initialize_node_values(struct olsrv2_tc_node *, bool raw); static void _initialize_attached_network_values(struct olsrv2_tc_attachment *edge); static void _initialize_edge_values(struct olsrv2_tc_edge *edge); static void _initialize_route_values(struct olsrv2_routing_entry *route); @@ -466,10 +466,10 @@ _initialize_originator_values(int af_type) { * @param entry originator set entry */ static void -_initialize_old_originator_values(struct olsrv2_originator_set_entry *entry) { +_initialize_old_originator_values(struct olsrv2_originator_set_entry *entry, bool raw) { netaddr_to_string(&_value_old_originator, &entry->originator); - oonf_timer_to_string(&_value_old_originator_vtime, &entry->_vtime); + oonf_timer_to_string_ext(&_value_old_originator_vtime, &entry->_vtime, raw); } /** @@ -539,10 +539,10 @@ _initialize_lan_values(struct olsrv2_lan_entry *lan) { * @param node OLSRv2 node */ static void -_initialize_node_values(struct olsrv2_tc_node *node) { +_initialize_node_values(struct olsrv2_tc_node *node, bool raw) { netaddr_to_string(&_value_node, &node->target.prefix.dst); - oonf_timer_to_string(&_value_node_vtime, &node->_validity_time); + oonf_timer_to_string_ext(&_value_node_vtime, &node->_validity_time, raw); snprintf(_value_node_ansn, sizeof(_value_node_ansn), "%u", node->ansn); @@ -604,7 +604,7 @@ _cb_create_text_old_originator(struct oonf_viewer_template *template) { struct olsrv2_originator_set_entry *entry; avl_for_each_element(olsrv2_originator_get_tree(), entry, _node) { - _initialize_old_originator_values(entry); + _initialize_old_originator_values(entry, template->create_raw); /* generate template output */ oonf_viewer_output_print_line(template); @@ -666,7 +666,7 @@ _cb_create_text_node(struct oonf_viewer_template *template) { struct olsrv2_tc_node *node; avl_for_each_element(olsrv2_tc_get_tree(), node, _originator_node) { - _initialize_node_values(node); + _initialize_node_values(node, template->create_raw); oonf_viewer_output_print_line(template); } @@ -685,7 +685,7 @@ _cb_create_text_attached_network(struct oonf_viewer_template *template) { struct nhdp_domain *domain; avl_for_each_element(olsrv2_tc_get_tree(), node, _originator_node) { - _initialize_node_values(node); + _initialize_node_values(node, template->create_raw); if (olsrv2_tc_is_node_virtual(node)) { continue; @@ -719,7 +719,7 @@ _cb_create_text_edge(struct oonf_viewer_template *template) { uint32_t metric; avl_for_each_element(olsrv2_tc_get_tree(), node, _originator_node) { - _initialize_node_values(node); + _initialize_node_values(node, template->create_raw); if (olsrv2_tc_is_node_virtual(node)) { continue; From ee66c724359fd71283157548788b9238a4929b10 Mon Sep 17 00:00:00 2001 From: Henning Rogge Date: Wed, 23 Oct 2019 09:34:01 +0200 Subject: [PATCH 30/63] Add memory guards and class ID to OONF class manager --- include/oonf/base/oonf_class.h | 87 +++++++++++++ .../oonf/base/os_linux/os_system_linux_data.h | 19 +++ include/oonf/libcore/oonf_logging.h | 2 +- src/base/oonf_class.c | 118 +++++++++++++++++- src/base/os_linux/os_system_linux.c | 36 +++++- 5 files changed, 255 insertions(+), 7 deletions(-) diff --git a/include/oonf/base/oonf_class.h b/include/oonf/base/oonf_class.h index ad9667fe..369f61d4 100644 --- a/include/oonf/base/oonf_class.h +++ b/include/oonf/base/oonf_class.h @@ -76,6 +76,44 @@ struct oonf_objectkey_str { char buf[128]; }; +/* storage data for a custom guard */ +struct oonf_class_guard { + const char *name; + uint32_t id; +}; + +enum { + OONF_CLASS_GUARD1 = 0x13572468, + OONF_CLASS_GUARD2 = 0x75318642 +}; + +/** + * Prefix to check data for overwriting and type error + */ +struct oonf_class_guard_prefix { + uint32_t id; + uint32_t guard1; +}; + +#ifdef OONF_LOG_DEBUG_INFO +#define OONF_CLASS_GUARD_PREFIX struct oonf_class_guard_prefix __guard_prefix; +#else +#define OONF_CLASS_GUARD_PREFIX +#endif + +/** + * Suffix to check data for overwriting + */ +struct oonf_class_guard_suffix { + uint32_t guard2; +}; + +#ifdef OONF_LOG_DEBUG_INFO +#define OONF_CLASS_GUARD_SUFFIX struct oonf_class_guard_suffix __guard_suffix; +#else +#define OONF_CLASS_GUARD_SUFFIX +#endif + /** * This structure represents a class of memory object, each with the same size. */ @@ -124,6 +162,12 @@ struct oonf_class { /*! Stats, recycled memory blocks */ uint32_t _recycled; + + /*! track debug status of class */ + bool debug; + + /* guard for debugging */ + struct oonf_class_guard class_guard; }; /** @@ -174,6 +218,9 @@ EXPORT void oonf_class_remove(struct oonf_class *); EXPORT void *oonf_class_malloc(struct oonf_class *) __attribute__((warn_unused_result)); EXPORT void oonf_class_free(struct oonf_class *, void *); +EXPORT void oonf_class_check(struct oonf_class *ci, void *ptr); + +EXPORT void oonf_class_guard_add(struct oonf_class_guard *); EXPORT int oonf_class_extension_add(struct oonf_class_extension *); EXPORT void oonf_class_extension_remove(struct oonf_class_extension *); @@ -248,4 +295,44 @@ oonf_class_is_extension_registered(struct oonf_class_extension *ext) { return list_is_node_added(&ext->_node); } +#ifdef OONF_LOG_DEBUG_INFO +#define oonf_class_guard_init(guard, base) oonf_class_guard_init_ext(guard, &(base)->__guard_prefix, &(base)->__guard_suffix) + +static INLINE void +oonf_class_guard_init_ext(struct oonf_class_guard *guard, + struct oonf_class_guard_prefix *prefix, struct oonf_class_guard_suffix *suffix) { + prefix->guard1 = OONF_CLASS_GUARD1; + prefix->id = guard->id; + suffix->guard2 = OONF_CLASS_GUARD2; +} + +#define oonf_class_guard_is_valid(guard, base) oonf_class_guard_is_valid_ext(guard, &(base)->__guard_prefix, &(base)->__guard_suffix) +#define OONF_CLASS_GUARD_ASSERT(guard, base, logging) OONF_ASSERT(oonf_class_guard_is_valid(guard, base), logging, "%s (%u) guard is bad (id=%u, guard1=%08x, guard2=%08x)", (guard)->name, (guard)->id, (base)->__guard_prefix.id, (base)->__guard_prefix.guard1, (base)->__guard_suffix.guard2) + +static INLINE bool +oonf_class_guard_is_valid_ext(struct oonf_class_guard *guard, + struct oonf_class_guard_prefix *prefix, struct oonf_class_guard_suffix *suffix) { + return prefix->guard1 == OONF_CLASS_GUARD1 + && suffix->guard2 == OONF_CLASS_GUARD2 + && prefix->id == guard->id; +} +#else /* OONF_LOG_DEBUG_INFO */ +#define oonf_class_guard_init(guard, base) do {} while(0) +static INLINE void +oonf_class_guard_init_ext(struct oonf_class_guard *guard __attribute__((unused)), + struct oonf_class_guard_prefix *prefix __attribute__((unused)), + struct oonf_class_guard_suffix *suffix __attribute__((unused))) { +} +#define oonf_class_guard_is_valid(guard, base) true +#define OONF_CLASS_GUARD_ASSERT(guard, ptr, logging) do {} while(0) + +static INLINE bool +oonf_class_guard_is_valid_ext(struct oonf_class_guard *guard __attribute__((unused)), + struct oonf_class_guard_prefix *prefix __attribute__((unused)), + struct oonf_class_guard_suffix *suffix __attribute__((unused))) { + return true; +} + +#endif /* OONF_LOG_DEBUG_INFO */ + #endif /* _OONF_CLASS_H */ diff --git a/include/oonf/base/os_linux/os_system_linux_data.h b/include/oonf/base/os_linux/os_system_linux_data.h index 812a4e7f..a088b111 100644 --- a/include/oonf/base/os_linux/os_system_linux_data.h +++ b/include/oonf/base/os_linux/os_system_linux_data.h @@ -56,6 +56,7 @@ struct os_system_netlink; #include #include #include +#include #include #include @@ -66,6 +67,9 @@ struct os_system_netlink; * Message for transfer to netlink subsystem */ struct os_system_netlink_message { + /* object guard for debugging */ + OONF_CLASS_GUARD_PREFIX; + /*! pointer to buffer with netlink message */ struct nlmsghdr *message; @@ -83,12 +87,18 @@ struct os_system_netlink_message { /*! hook into list of messages, either buffered or sent */ struct list_entity _node; + + /* object guard for debugging */ + OONF_CLASS_GUARD_SUFFIX; }; /** * Centralized socket for all users of a certain netlink family type */ struct os_system_netlink_socket { + /* object guard for debugging */ + OONF_CLASS_GUARD_PREFIX; + /*! NETLINK_xxx type socket */ int32_t netlink_type; @@ -118,11 +128,17 @@ struct os_system_netlink_socket { /*! hook into tree of netlink socket */ struct avl_node _node; + + /* object guard for debugging */ + OONF_CLASS_GUARD_SUFFIX; }; /** * Linux netlink handler */ struct os_system_netlink { + /* object guard for debugging */ + OONF_CLASS_GUARD_PREFIX; + /*! name of netlink handler */ const char *name; @@ -176,6 +192,9 @@ struct os_system_netlink { /*! hook into list of handlers for netlink protocol */ struct list_entity _node; + + /* object guard for debugging */ + OONF_CLASS_GUARD_SUFFIX; }; #endif /* OS_SYSTEM_LINUX_DATA_H_ */ diff --git a/include/oonf/libcore/oonf_logging.h b/include/oonf/libcore/oonf_logging.h index fc8b869e..6bdca2e5 100644 --- a/include/oonf/libcore/oonf_logging.h +++ b/include/oonf/libcore/oonf_logging.h @@ -342,7 +342,7 @@ struct oonf_log_parameters { * @param format printf style format string * @param args variable number of parameters for format string */ -#define OONF_ASSERT_HEX(condition, source, format, args...) _OONF_LOG(LOG_SEVERITY_ASSERT, source, hexptr, hexlen, !(condition), true, format, ##args) +#define OONF_ASSERT_HEX(condition, source, hexptr, hexlen, format, args...) _OONF_LOG(LOG_SEVERITY_ASSERT, source, hexptr, hexlen, !(condition), true, format, ##args) /** * Definition of a logging handler diff --git a/src/base/oonf_class.c b/src/base/oonf_class.c index e4f63c4b..a94c882f 100644 --- a/src/base/oonf_class.c +++ b/src/base/oonf_class.c @@ -55,6 +55,10 @@ /* Definitions */ #define LOG_CLASS (_oonf_class_subsystem.logging) +struct _class_config { + bool debug; +}; + /* prototypes */ static int _init(void); static void _cleanup(void); @@ -63,6 +67,8 @@ static void _free_freelist(struct oonf_class *); static size_t _roundup(size_t); static const char *_cb_to_keystring(struct oonf_objectkey_str *, struct oonf_class *, void *); +static void _cb_cfg_class_changed(void); + /* list of memory cookies */ static struct avl_tree _classes_tree; @@ -73,14 +79,37 @@ static const char *OONF_CLASS_EVENT_NAME[] = { [OONF_OBJECT_CHANGED] = "changed", }; +static struct cfg_schema_entry _class_entries[] = { + CFG_MAP_BOOL( + _class_config, debug, "debug", "false", "True to enable additional debugging code for memory allocation" + ) +}; + +static struct cfg_schema_section _class_section = { + .type = OONF_CLASS_SUBSYSTEM, + .mode = CFG_SSMODE_UNNAMED, + + .cb_delta_handler = _cb_cfg_class_changed, + .entries = _class_entries, + .entry_count = ARRAYSIZE(_class_entries), +}; + + /* subsystem definition */ static struct oonf_subsystem _oonf_class_subsystem = { .name = OONF_CLASS_SUBSYSTEM, .init = _init, .cleanup = _cleanup, + .cfg_section = &_class_section, }; DECLARE_OONF_PLUGIN(_oonf_class_subsystem); +/* debug configuration */ +static struct _class_config _config; +static uint32_t _next_debug_id = 1; +static const size_t _debug_size = + sizeof(struct oonf_class_guard_prefix) + sizeof(struct oonf_class_guard_suffix); + /** * Initialize the class system * @return always returns 0 @@ -128,7 +157,12 @@ oonf_class_add(struct oonf_class *ci) { list_init_head(&ci->_free_list); list_init_head(&ci->_extensions); - OONF_DEBUG(LOG_CLASS, "Class %s added: %" PRINTF_SIZE_T_SPECIFIER " bytes\n", ci->name, ci->total_size); + /* debug settings */ + ci->debug = _config.debug; + + oonf_class_guard_add(&ci->class_guard); + + OONF_DEBUG(LOG_CLASS, "Class %s (id=%u) added: %" PRINTF_SIZE_T_SPECIFIER " bytes\n", ci->name, ci->class_guard.id, ci->total_size); } /** @@ -161,8 +195,9 @@ oonf_class_remove(struct oonf_class *ci) { void * oonf_class_malloc(struct oonf_class *ci) { struct list_entity *entity; + struct oonf_class_guard_prefix *prefix; + struct oonf_class_guard_suffix *suffix; void *ptr; - #ifdef OONF_LOG_DEBUG_INFO bool reuse = false; #endif @@ -175,7 +210,12 @@ oonf_class_malloc(struct oonf_class *ci) { * No reusable memory block on the free_list. * Allocate a fresh one. */ - ptr = calloc(1, ci->total_size); + if (ci->debug) { + ptr = calloc(1, ci->total_size + _debug_size); + } + else { + ptr = calloc(1, ci->total_size); + } if (ptr == NULL) { OONF_WARN(LOG_CLASS, "Out of memory for: %s", ci->name); return NULL; @@ -205,7 +245,17 @@ oonf_class_malloc(struct oonf_class *ci) { OONF_DEBUG(LOG_CLASS, "MEMORY: alloc %s, %" PRINTF_SIZE_T_SPECIFIER " bytes%s\n", ci->name, ci->total_size, reuse ? ", reuse" : ""); - return ptr; + + if (!ci->debug) { + return ptr; + } + + /* handle debug initialization */ + prefix = ptr; + suffix = (void *)((uint8_t*)ptr + sizeof(struct oonf_class_guard_prefix) + ci->total_size); + + oonf_class_guard_init_ext(&ci->class_guard, prefix, suffix); + return &prefix[1]; } /** @@ -220,12 +270,17 @@ oonf_class_free(struct oonf_class *ci, void *ptr) { bool reuse = false; #endif + if (ci->debug) { + oonf_class_check(ci, ptr); + } + /* * Rather than freeing the memory right away, try to reuse at a later * point. Keep at least ten percent of the active used blocks or at least * ten blocks on the free list. */ - if (ci->_free_list_size < ci->min_free_count || (ci->_free_list_size < ci->_current_usage / 10)) { + if (!ci->debug && + (ci->_free_list_size < ci->min_free_count || (ci->_free_list_size < ci->_current_usage / 10))) { item = ptr; list_add_tail(&ci->_free_list, item); @@ -247,6 +302,36 @@ oonf_class_free(struct oonf_class *ci, void *ptr) { LOG_CLASS, "MEMORY: free %s, %" PRINTF_SIZE_T_SPECIFIER " bytes%s\n", ci->name, ci->size, reuse ? ", reuse" : ""); } +void +oonf_class_guard_add(struct oonf_class_guard *guard) { + guard->id = _next_debug_id; + _next_debug_id++; +} + +/** + * Check if a memory block has still the right debug constraints. + * Will end program with an assert if block is wrong. + * @param ci class info + * @param ptr pointer to memory block + */ +void +oonf_class_check(struct oonf_class *ci, void *ptr) { + struct oonf_class_guard_prefix *prefix; + struct oonf_class_guard_suffix *suffix; + + if (!ci->debug) { + /* don't check */ + return; + } + + prefix = (void *)((uint8_t *)ptr - sizeof(struct oonf_class_guard_prefix)); + suffix = (void *)((uint8_t *)ptr + ci->total_size); + + OONF_ASSERT_HEX(oonf_class_guard_is_valid_ext(&ci->class_guard, prefix, suffix), LOG_CLASS, prefix, sizeof(prefix), + "class '%s' (id=%u): guard is bad (id=%u, g1=%08x, g2=%08x)", ci->name, ci->class_guard.id, + prefix->id, prefix->guard1, suffix->guard2); +} + /** * Register an extension to an existing class without objects. * This function can only fail if ext->size is not 0. @@ -398,3 +483,26 @@ _cb_to_keystring(struct oonf_objectkey_str *buf, struct oonf_class *class, void return buf->buf; } + +static void +_cb_cfg_class_changed(void) { + struct oonf_class *info, *iterator; + struct _class_config config; + int result; + + memset(&config, 0, sizeof(config)); + result = cfg_schema_tobin(&config, _class_section.post, _class_entries, ARRAYSIZE(_class_entries)); + if (result) { + OONF_WARN(LOG_CLASS, "Could not convert %s to binary (%d)", _class_section.type, -(result + 1)); + return; + } + + avl_for_each_element_safe(&_classes_tree, info, _node, iterator) { + if (config.debug != info->debug) { + if (info->_allocated == 0) { + info->debug = config.debug; + _free_freelist(info); + } + } + } +} diff --git a/src/base/os_linux/os_system_linux.c b/src/base/os_linux/os_system_linux.c index 65997c07..1d8bbd23 100644 --- a/src/base/os_linux/os_system_linux.c +++ b/src/base/os_linux/os_system_linux.c @@ -174,6 +174,17 @@ static struct oonf_class _netlink_protocol_class = { .size = sizeof(struct os_system_netlink_socket), }; +/* object guards for debugging */ +static struct oonf_class_guard _netlink_message_guard = { + .name = "netlink message" +}; +static struct oonf_class_guard _netlink_socket_guard = { + .name = "netlink socket" +}; +static struct oonf_class_guard _netlink_guard = { + .name = "netlink handler" +}; + /** * Initialize os-specific subsystem * @return -1 if an error happened, 0 otherwise @@ -201,6 +212,10 @@ _init(void) { oonf_timer_add(&_netlink_timer); avl_init(&_netlink_protocol_tree, avl_comp_int32, false); oonf_class_add(&_netlink_protocol_class); + + oonf_class_guard_add(&_netlink_message_guard); + oonf_class_guard_add(&_netlink_guard); + oonf_class_guard_add(&_netlink_socket_guard); return 0; } @@ -320,8 +335,9 @@ os_system_linux_netlink_add(struct os_system_netlink *nl, int protocol) { return -1; } } - + list_add_tail(&nl->nl_socket->handlers, &nl->_node); + oonf_class_guard_init(&_netlink_guard, nl); return 0; } @@ -334,6 +350,7 @@ os_system_linux_netlink_remove(struct os_system_netlink *nl) { struct os_system_netlink_socket *nl_socket; nl_socket = nl->nl_socket; + OONF_CLASS_GUARD_ASSERT(&_netlink_guard, nl, nl->used_by->logging); list_remove(&nl->_node); if (!list_is_empty(&nl_socket->handlers)) { return; @@ -378,6 +395,8 @@ os_system_linux_netlink_send(struct os_system_netlink *nl, struct os_system_netl oonf_socket_set_write(&nl_socket->nl_socket, true); } list_add_tail(&nl->nl_socket->buffered_messages, &msg->_node); + + oonf_class_guard_init(&_netlink_message_guard, msg); } /** @@ -497,6 +516,8 @@ _add_protocol(int32_t protocol) { list_init_head(&nl_sock->buffered_messages); list_init_head(&nl_sock->sent_messages); list_init_head(&nl_sock->handlers); + + oonf_class_guard_init(&_netlink_socket_guard, nl_sock); return nl_sock; os_add_netlink_fail: @@ -514,6 +535,7 @@ _add_protocol(int32_t protocol) { */ static void _remove_protocol(struct os_system_netlink_socket *nl_socket) { + OONF_CLASS_GUARD_ASSERT(&_netlink_socket_guard, nl_socket, LOG_OS_SYSTEM); if (os_fd_is_initialized(&nl_socket->nl_socket.fd)) { oonf_socket_remove(&nl_socket->nl_socket); @@ -534,8 +556,10 @@ _cb_handle_netlink_timeout(struct oonf_timer_instance *ptr) { struct os_system_netlink_message *msg, *msg_it; nl_socket = container_of(ptr, struct os_system_netlink_socket, timeout); + OONF_CLASS_GUARD_ASSERT(&_netlink_socket_guard, nl_socket, LOG_OS_SYSTEM); list_for_each_element_safe(&nl_socket->sent_messages, msg, _node, msg_it) { + OONF_CLASS_GUARD_ASSERT(&_netlink_message_guard, msg, LOG_OS_SYSTEM); if (msg->originator->cb_error) { msg->originator->cb_error(msg); } @@ -557,6 +581,7 @@ _send_netlink_messages(struct os_system_netlink_socket *nl_socket) { struct nlmsghdr *nl_hdr; ssize_t ret; int err; + if (!list_is_empty(&nl_socket->sent_messages)) { /* still messages in transit */ return; @@ -571,6 +596,8 @@ _send_netlink_messages(struct os_system_netlink_socket *nl_socket) { nl_msg = list_first_element(&nl_socket->buffered_messages, nl_msg, _node); do { + OONF_CLASS_GUARD_ASSERT(&_netlink_message_guard, nl_msg, LOG_OS_SYSTEM); + _netlink_send_iov[count].iov_base = nl_msg->message; _netlink_send_iov[count].iov_len = nl_msg->message->nlmsg_len; @@ -620,6 +647,8 @@ _send_netlink_messages(struct os_system_netlink_socket *nl_socket) { /* report error */ list_for_each_element_safe(&nl_socket->sent_messages, nl_msg, _node, nl_msg_it) { + OONF_CLASS_GUARD_ASSERT(&_netlink_message_guard, nl_msg, LOG_OS_SYSTEM); + list_remove(&nl_msg->_node); nl_msg->result = err; if (nl_msg->originator->cb_error) { @@ -630,6 +659,7 @@ _send_netlink_messages(struct os_system_netlink_socket *nl_socket) { else { /* just try again later, shuffle messages back to transmission queue */ list_for_each_element_reverse_safe(&nl_socket->sent_messages, nl_msg, _node, nl_msg_it) { + OONF_CLASS_GUARD_ASSERT(&_netlink_message_guard, nl_msg, LOG_OS_SYSTEM); list_remove(&nl_msg->_node); list_add_head(&nl_socket->buffered_messages, &nl_msg->_node); } @@ -656,6 +686,7 @@ _find_matching_message(struct os_system_netlink_socket *nl_socket, uint32_t seqn struct os_system_netlink_message *nl_msg; list_for_each_element(&nl_socket->sent_messages, nl_msg, _node) { + OONF_CLASS_GUARD_ASSERT(&_netlink_message_guard, nl_msg, LOG_OS_SYSTEM); if (nl_msg->message->nlmsg_seq == seqno) { return nl_msg; } @@ -679,6 +710,8 @@ _netlink_handler(struct oonf_socket_entry *entry) { int flags; nl_socket = container_of(entry, typeof(*nl_socket), nl_socket); + OONF_CLASS_GUARD_ASSERT(&_netlink_socket_guard, nl_socket, LOG_OS_SYSTEM); + if (oonf_socket_is_write(entry)) { _send_netlink_messages(nl_socket); } @@ -792,6 +825,7 @@ _netlink_handler(struct oonf_socket_entry *entry) { else { /* this seems to be multicast */ list_for_each_element(&nl_socket->handlers, nl_handler, _node) { + OONF_CLASS_GUARD_ASSERT(&_netlink_guard, nl_handler, LOG_OS_SYSTEM); for (i=0; imulticast_message_count; i++) { if (nl_handler->multicast_messages[i] == nh->nlmsg_type) { nl_handler->cb_multicast(nl_handler, nh); From fd78be9065cfb9eee4d693e59f32d38a198da33d Mon Sep 17 00:00:00 2001 From: Henning Rogge Date: Tue, 21 Jan 2020 10:49:24 +0100 Subject: [PATCH 31/63] Fix segfault in DLEP extension deactivation --- src/generic/dlep/dlep_session.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/generic/dlep/dlep_session.c b/src/generic/dlep/dlep_session.c index d4cf8d25..46115b10 100644 --- a/src/generic/dlep/dlep_session.c +++ b/src/generic/dlep/dlep_session.c @@ -256,10 +256,10 @@ dlep_session_update_extensions(struct dlep_session *session, const uint8_t *extv } if (deactivate) { - if (radio) { + if (radio && session->parser.extensions[j]->cb_session_deactivate_radio != NULL) { session->parser.extensions[j]->cb_session_deactivate_radio(session); } - else { + if (!radio && session->parser.extensions[j]->cb_session_deactivate_router != NULL) { session->parser.extensions[j]->cb_session_deactivate_router(session); } } @@ -318,7 +318,8 @@ dlep_session_process_tcp(struct oonf_stream_session *tcp_session, struct dlep_se oonf_stream_flush(tcp_session); } - if (session->restrict_signal == DLEP_KILL_SESSION) { + if (session->restrict_signal == DLEP_KILL_SESSION + || session->_peer_state == DLEP_PEER_TERMINATED) { return STREAM_SESSION_CLEANUP; } return STREAM_SESSION_ACTIVE; From ff167f9e3842226b87919b9ea2a65a20275be498 Mon Sep 17 00:00:00 2001 From: Henning Rogge Date: Tue, 4 Feb 2020 13:14:18 +0100 Subject: [PATCH 32/63] Fix DLEP IANA values according to latest registry --- include/oonf/generic/dlep/dlep_iana.h | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/include/oonf/generic/dlep/dlep_iana.h b/include/oonf/generic/dlep/dlep_iana.h index 98dc1889..de5e0d8d 100644 --- a/include/oonf/generic/dlep/dlep_iana.h +++ b/include/oonf/generic/dlep/dlep_iana.h @@ -252,11 +252,26 @@ enum dlep_tlvs /*! MTU of interface */ DLEP_MTU_TLV = 20, + /*! Hopcount */ + DLEP_HOPCOUNT_TLV = 21, + + /* Hopcount request */ + DLEP_HOPCONTROL_TLV = 22, + + /* Queue parameters */ + DLEP_QUEUE_PARAMETER_TLV = 23, + + /* Queue pause */ + DLEP_QUEUE_PAUSE_TLV = 24, + + /* Queue pause */ + DLEP_QUEUE_RESTART_TLV = 25, + /*! link ID TLV */ - DLEP_LID_TLV = 21, + DLEP_LID_TLV = 26, /*! link ID TLV */ - DLEP_LID_LENGTH_TLV = 22, + DLEP_LID_LENGTH_TLV = 27, /* l1 statistics */ From 226ed497adecc3803bd00e467b523e22e05153f4 Mon Sep 17 00:00:00 2001 From: Henning Rogge Date: Fri, 14 Feb 2020 14:04:42 +0100 Subject: [PATCH 33/63] Add new delayed callback subsystem Add a delayed callback subsystem to break a cycle between rfc5444 reconfiguration and packet socket reconfiguration. --- apps/dlep-radio/CMakeLists.txt | 1 + apps/dlep-router/CMakeLists.txt | 1 + apps/olsrd2-dlep/CMakeLists.txt | 1 + apps/olsrd2/CMakeLists.txt | 1 + include/oonf/base/oonf_callback.h | 78 ++++++++++++++ include/oonf/base/oonf_packet_socket.h | 7 ++ src/base/CMakeLists.txt | 3 +- src/base/oonf_callback.c | 143 +++++++++++++++++++++++++ src/base/oonf_packet_socket.c | 23 +++- src/base/oonf_rfc5444.c | 22 +++- src/base/oonf_socket.c | 4 + 11 files changed, 279 insertions(+), 5 deletions(-) create mode 100644 include/oonf/base/oonf_callback.h create mode 100644 src/base/oonf_callback.c diff --git a/apps/dlep-radio/CMakeLists.txt b/apps/dlep-radio/CMakeLists.txt index 016b6765..42ce6766 100644 --- a/apps/dlep-radio/CMakeLists.txt +++ b/apps/dlep-radio/CMakeLists.txt @@ -28,6 +28,7 @@ set (OONF_APP_DEFAULT_CFG_HANDLER Compact) IF (NOT OONF_STATIC_PLUGINS) set (OONF_STATIC_PLUGINS class + callback clock layer2 packet_socket diff --git a/apps/dlep-router/CMakeLists.txt b/apps/dlep-router/CMakeLists.txt index 5c5aed2f..a205c70c 100644 --- a/apps/dlep-router/CMakeLists.txt +++ b/apps/dlep-router/CMakeLists.txt @@ -28,6 +28,7 @@ set (OONF_APP_DEFAULT_CFG_HANDLER Compact) IF (NOT OONF_STATIC_PLUGINS) set (OONF_STATIC_PLUGINS class + callback clock layer2 packet_socket diff --git a/apps/olsrd2-dlep/CMakeLists.txt b/apps/olsrd2-dlep/CMakeLists.txt index f300af02..894b6f86 100644 --- a/apps/olsrd2-dlep/CMakeLists.txt +++ b/apps/olsrd2-dlep/CMakeLists.txt @@ -28,6 +28,7 @@ set (OONF_APP_DEFAULT_CFG_HANDLER Compact) IF (NOT OONF_STATIC_PLUGINS) set (OONF_STATIC_PLUGINS class + callback clock duplicate_set layer2 diff --git a/apps/olsrd2/CMakeLists.txt b/apps/olsrd2/CMakeLists.txt index 4405c83b..3375b20f 100644 --- a/apps/olsrd2/CMakeLists.txt +++ b/apps/olsrd2/CMakeLists.txt @@ -28,6 +28,7 @@ set (OONF_APP_DEFAULT_CFG_HANDLER Compact) IF (NOT OONF_STATIC_PLUGINS) set (OONF_STATIC_PLUGINS class + callback clock duplicate_set layer2 diff --git a/include/oonf/base/oonf_callback.h b/include/oonf/base/oonf_callback.h new file mode 100644 index 00000000..6e421f21 --- /dev/null +++ b/include/oonf/base/oonf_callback.h @@ -0,0 +1,78 @@ + +/* + * The olsr.org Optimized Link-State Routing daemon version 2 (olsrd2) + * Copyright (c) 2004-2015, the olsr.org team - see HISTORY file + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of olsr.org, olsrd nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * Visit http://www.olsr.org for more information. + * + * If you find this software useful feel free to make a donation + * to the project. For more information see the website or contact + * the copyright holders. + * + */ + +/** + * @file + */ + +#ifndef OONF_CALLBACK_H_ +#define OONF_CALLBACK_H_ + +#include +#include + +/*! subsystem identifier */ +#define OONF_CALLBACK_SUBSYSTEM "callback" + +/** + * This struct defines callback that should be called as soon as + * the runtime returns to the mainloop. + */ +struct oonf_callback { + /*! node of callback list */ + struct list_entity _node; + + /*! name of this callback */ + const char *name; + + /** + * Callback when timer is triggered + * @param ptr pointer to timer instance that fired + */ + void (*cb_trigger)(struct oonf_callback *ptr); +}; + +EXPORT void oonf_callback_add(struct oonf_callback *cb); +EXPORT void oonf_callback_remove(struct oonf_callback *cb); +EXPORT void oonf_callback_walk(void); +EXPORT struct list_entity *oonf_callback_get_list(void); + +#endif /* OONF_CALLBACK_H_ */ diff --git a/include/oonf/base/oonf_packet_socket.h b/include/oonf/base/oonf_packet_socket.h index 10286c4a..73bb218f 100644 --- a/include/oonf/base/oonf_packet_socket.h +++ b/include/oonf/base/oonf_packet_socket.h @@ -51,6 +51,7 @@ #include #include #include +#include #include #include @@ -210,6 +211,12 @@ struct oonf_packet_managed { */ void (*cb_settings_change)(struct oonf_packet_managed *managed, bool changed); + /*! helper for delayed change callback handling */ + struct oonf_callback _change_callback; + + /*! parameter for delayed change callback */ + bool _change_callback_flag; + /*! configuration of managed socket */ struct oonf_packet_managed_config _managed_config; diff --git a/src/base/CMakeLists.txt b/src/base/CMakeLists.txt index 4e36c630..26619bbc 100644 --- a/src/base/CMakeLists.txt +++ b/src/base/CMakeLists.txt @@ -1,5 +1,6 @@ # create plugins for single-file (source plus header) subsystems -SET(SINGLE_FILE_NAMES class +SET(SINGLE_FILE_NAMES callback + class clock duplicate_set http diff --git a/src/base/oonf_callback.c b/src/base/oonf_callback.c new file mode 100644 index 00000000..166677ef --- /dev/null +++ b/src/base/oonf_callback.c @@ -0,0 +1,143 @@ + +/* + * The olsr.org Optimized Link-State Routing daemon version 2 (olsrd2) + * Copyright (c) 2004-2015, the olsr.org team - see HISTORY file + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of olsr.org, olsrd nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * Visit http://www.olsr.org for more information. + * + * If you find this software useful feel free to make a donation + * to the project. For more information see the website or contact + * the copyright holders. + * + */ + +/** + * @file + */ + +#include +#include +#include +#include + +/* definitions */ +#define LOG_CALLBACK _oonf_callback_subsystem.logging + +/* prototypes */ +static int _init(void); +static void _cleanup(void); + +static struct oonf_subsystem _oonf_callback_subsystem = { + .name = OONF_CALLBACK_SUBSYSTEM, + .init = _init, + .cleanup = _cleanup, +}; +DECLARE_OONF_PLUGIN(_oonf_callback_subsystem); + +/* static list of callbacks */ +static struct list_entity _callback_list; + +/*! static reminder that a callback is running */ +static struct oonf_callback *_callback_in_progress; + +/** + * Initialize olsr callback system + * @return always 0 + */ +static int +_init(void) { + list_init_head(&_callback_list); + return 0; +} + +/** + * Cleanup existing callback system datastructures + */ +static void +_cleanup(void) { + struct oonf_callback *cb, *cb_it; + + list_for_each_element_safe(&_callback_list, cb, _node, cb_it) { + oonf_callback_remove(cb); + } +} + +/** + * Add a callback to the list + * @return -1 if an error happened, 0 otherwise + */ +void +oonf_callback_add(struct oonf_callback *cb) { + OONF_ASSERT(!_callback_in_progress, LOG_CALLBACK, + "Error, callback %s is trying to add another callback", + _callback_in_progress->name); + + if (list_is_node_added(&cb->_node)) { + list_remove(&cb->_node); + } + list_add_tail(&_callback_list, &cb->_node); +} + +/** + * Add a callback to the list + * @return -1 if an error happened, 0 otherwise + */ +void +oonf_callback_remove(struct oonf_callback *cb) { + if (list_is_node_added(&cb->_node)) { + list_remove(&cb->_node); + } +} + +/** + * Call all registered callbacks + */ +void +oonf_callback_walk(void) { + struct oonf_callback *cb; + + while (!list_is_empty(&_callback_list)) { + cb = list_first_element(&_callback_list, cb, _node); + oonf_callback_remove(cb); + + _callback_in_progress = cb; + cb->cb_trigger(cb); + _callback_in_progress = NULL; + } +} + +/** + * @returns list of all callbacks + */ +struct list_entity * +oonf_callback_get_list(void) { + return &_callback_list; +} diff --git a/src/base/oonf_packet_socket.c b/src/base/oonf_packet_socket.c index 64c21813..10719a7a 100644 --- a/src/base/oonf_packet_socket.c +++ b/src/base/oonf_packet_socket.c @@ -76,6 +76,7 @@ static void _cb_packet_event_unicast(struct oonf_socket_entry *); static void _cb_packet_event_multicast(struct oonf_socket_entry *); static void _cb_packet_event(struct oonf_socket_entry *, bool mc); static int _cb_interface_listener(struct os_interface_listener *l); +static void _cb_delayed_change(struct oonf_callback *cb); /* subsystem definition */ static const char *_dependencies[] = { @@ -263,6 +264,8 @@ oonf_packet_send(struct oonf_packet_socket *pktsocket, union netaddr_socket *rem */ void oonf_packet_add_managed(struct oonf_packet_managed *managed) { + static const char CHANGE_CALLBACK[] = "oonf_packet change"; + if (managed->config.input_buffer_length == 0) { managed->config.input_buffer = _input_buffer; managed->config.input_buffer_length = sizeof(_input_buffer); @@ -271,6 +274,9 @@ oonf_packet_add_managed(struct oonf_packet_managed *managed) { managed->_if_listener.if_changed = _cb_interface_listener; managed->_if_listener.name = managed->_managed_config.interface; managed->_if_listener.mesh = managed->_managed_config.mesh; + + managed->_change_callback.cb_trigger = _cb_delayed_change; + managed->_change_callback.name = CHANGE_CALLBACK; } /** @@ -280,6 +286,7 @@ oonf_packet_add_managed(struct oonf_packet_managed *managed) { */ void oonf_packet_remove_managed(struct oonf_packet_managed *managed, bool forced) { + oonf_callback_remove(&managed->_change_callback); oonf_packet_remove(&managed->socket_v4, forced); oonf_packet_remove(&managed->socket_v6, forced); oonf_packet_remove(&managed->multicast_v4, forced); @@ -540,11 +547,25 @@ _apply_managed(struct oonf_packet_managed *managed) { } if (managed->cb_settings_change) { - managed->cb_settings_change(managed, changed); + managed->_change_callback_flag |= changed; + oonf_callback_add(&managed->_change_callback); } return result; } +/** + * Trigger a delayed change handler + * @param cb callback that triggered + */ +static void +_cb_delayed_change(struct oonf_callback *cb) { + struct oonf_packet_managed *managed; + + managed = container_of(cb, struct oonf_packet_managed, _change_callback); + managed->cb_settings_change(managed, managed->_change_callback_flag); + managed->_change_callback_flag = false; +} + /** * Apply a new configuration to an unicast/multicast socket pair * @param managed managed socket diff --git a/src/base/oonf_rfc5444.c b/src/base/oonf_rfc5444.c index 842b0879..bb37adfc 100644 --- a/src/base/oonf_rfc5444.c +++ b/src/base/oonf_rfc5444.c @@ -96,6 +96,8 @@ static void _destroy_target(struct oonf_rfc5444_target *); static void _print_packet_to_buffer(enum oonf_log_source source, union netaddr_socket *sock, struct oonf_rfc5444_interface *interf, const uint8_t *ptr, size_t len, const char *success, const char *error); +void _reconfigure_interface(struct oonf_rfc5444_interface *interf, + struct oonf_packet_managed_config *config, bool ifchange_context); static void _cb_receive_data(struct oonf_packet_socket *, union netaddr_socket *from, void *ptr, size_t length); static void _cb_send_unicast_packet(struct rfc5444_writer *, struct rfc5444_writer_target *, void *, size_t); static void _cb_send_multicast_packet(struct rfc5444_writer *, struct rfc5444_writer_target *, void *, size_t); @@ -681,6 +683,18 @@ oonf_rfc5444_remove_interface(struct oonf_rfc5444_interface *interf, struct oonf */ void oonf_rfc5444_reconfigure_interface(struct oonf_rfc5444_interface *interf, struct oonf_packet_managed_config *config) { + _reconfigure_interface(interf, config, false); +} + +/** + * Reconfigure the parameters of an rfc5444 interface. You cannot reconfigure + * the interface name with this command. + * @param interf pointer to existing rfc5444 interface + * @param config new socket configuration, NULL to just reapply the current + * configuration + */ +void +_reconfigure_interface(struct oonf_rfc5444_interface *interf, struct oonf_packet_managed_config *config, bool ifchange_context) { struct oonf_rfc5444_target *target, *old; uint16_t port; struct netaddr_str buf; @@ -735,8 +749,10 @@ oonf_rfc5444_reconfigure_interface(struct oonf_rfc5444_interface *interf, struct return; } - /* apply socket configuration */ - oonf_packet_apply_managed(&interf->_socket, &interf->_socket_config); + if (!ifchange_context) { + /* apply socket configuration */ + oonf_packet_apply_managed(&interf->_socket, &interf->_socket_config); + } /* handle IPv4 multicast target */ if (interf->multicast4) { @@ -1446,7 +1462,7 @@ _cb_interface_changed(struct oonf_packet_managed *managed, bool changed) { interf = container_of(managed, struct oonf_rfc5444_interface, _socket); if (changed) { - oonf_rfc5444_reconfigure_interface(interf, NULL); + _reconfigure_interface(interf, NULL, true); } list_for_each_element(&interf->_listener, l, _node) { diff --git a/src/base/oonf_socket.c b/src/base/oonf_socket.c index b4da0a44..e0660659 100644 --- a/src/base/oonf_socket.c +++ b/src/base/oonf_socket.c @@ -53,6 +53,7 @@ #include #include #include +#include #include #include #include @@ -81,6 +82,7 @@ struct os_fd_select _socket_events; /* subsystem definition */ static const char *_dependencies[] = { + OONF_CALLBACK_SUBSYSTEM, OONF_TIMER_SUBSYSTEM, OONF_OS_FD_SUBSYSTEM, }; @@ -218,12 +220,14 @@ _handle_scheduling(void) { return -1; } + oonf_callback_walk(); oonf_timer_walk(); if (_shall_end_scheduler()) { return 0; } + oonf_callback_walk(); next_event = oonf_timer_getNextEvent(); if (next_event > _scheduler_time_limit) { next_event = _scheduler_time_limit; From c875bdcbc01cc140dae52d6232ef22181be1340b Mon Sep 17 00:00:00 2001 From: Henning Rogge Date: Fri, 14 Feb 2020 14:16:11 +0100 Subject: [PATCH 34/63] First set of DLEP fixes for clean shutdown --- include/oonf/generic/dlep/dlep_iana.h | 4 +- src/generic/dlep/dlep_interface.c | 24 ++++-------- src/generic/dlep/dlep_session.c | 27 ++++++++----- src/generic/dlep/dlep_telnet.c | 50 ++++++++++++++++++++++++- src/generic/dlep/ext_base_proto/proto.c | 12 ++++++ 5 files changed, 87 insertions(+), 30 deletions(-) diff --git a/include/oonf/generic/dlep/dlep_iana.h b/include/oonf/generic/dlep/dlep_iana.h index 98dc1889..2a9603b2 100644 --- a/include/oonf/generic/dlep/dlep_iana.h +++ b/include/oonf/generic/dlep/dlep_iana.h @@ -253,10 +253,10 @@ enum dlep_tlvs DLEP_MTU_TLV = 20, /*! link ID TLV */ - DLEP_LID_TLV = 21, + DLEP_LID_LENGTH_TLV = 26, /*! link ID TLV */ - DLEP_LID_LENGTH_TLV = 22, + DLEP_LID_TLV = 27, /* l1 statistics */ diff --git a/src/generic/dlep/dlep_interface.c b/src/generic/dlep/dlep_interface.c index ac27eacf..028cd600 100644 --- a/src/generic/dlep/dlep_interface.c +++ b/src/generic/dlep/dlep_interface.c @@ -272,24 +272,16 @@ _cb_send_multicast(struct dlep_session *session, int af_family) { /* get pointer to radio interface */ interf = container_of(session, struct dlep_if, session); - switch (interf->udp_mode) { - case DLEP_IF_UDP_NONE: - return; - case DLEP_IF_UDP_SINGLE_SESSION: - if(interf->session_tree.count > 0) { - return; - } - break; - default: - break; + if (interf->udp_mode == DLEP_IF_UDP_ALWAYS + || (interf->udp_mode == DLEP_IF_UDP_SINGLE_SESSION + && avl_is_empty(&interf->session_tree))) { + OONF_DEBUG( + session->log_source, "Send multicast %" PRINTF_SIZE_T_SPECIFIER " bytes", abuf_getlen(session->writer.out)); + + oonf_packet_send_managed_multicast( + &interf->udp, abuf_getptr(session->writer.out), abuf_getlen(session->writer.out), af_family); } - OONF_DEBUG( - session->log_source, "Send multicast %" PRINTF_SIZE_T_SPECIFIER " bytes", abuf_getlen(session->writer.out)); - - oonf_packet_send_managed_multicast( - &interf->udp, abuf_getptr(session->writer.out), abuf_getlen(session->writer.out), af_family); - abuf_clear(session->writer.out); /* add dlep prefix to buffer */ diff --git a/src/generic/dlep/dlep_session.c b/src/generic/dlep/dlep_session.c index 46115b10..56479721 100644 --- a/src/generic/dlep/dlep_session.c +++ b/src/generic/dlep/dlep_session.c @@ -302,10 +302,6 @@ dlep_session_process_tcp(struct oonf_stream_session *tcp_session, struct dlep_se return STREAM_SESSION_CLEANUP; } - if (session->restrict_signal == DLEP_KILL_SESSION) { - return STREAM_SESSION_CLEANUP; - } - OONF_DEBUG(session->log_source, "Processed %" PRINTF_SSIZE_T_SPECIFIER " bytes", processed); abuf_pull(&tcp_session->in, processed); @@ -318,8 +314,7 @@ dlep_session_process_tcp(struct oonf_stream_session *tcp_session, struct dlep_se oonf_stream_flush(tcp_session); } - if (session->restrict_signal == DLEP_KILL_SESSION - || session->_peer_state == DLEP_PEER_TERMINATED) { + if (session->restrict_signal == DLEP_KILL_SESSION) { return STREAM_SESSION_CLEANUP; } return STREAM_SESSION_ACTIVE; @@ -424,7 +419,9 @@ dlep_session_process_signal(struct dlep_session *session, const void *ptr, size_ "Process signal %d from %s (%" PRINTF_SIZE_T_SPECIFIER " bytes)", signal_type, netaddr_socket_to_string(&nbuf, &session->remote_socket), length); - if (session->restrict_signal != DLEP_ALL_SIGNALS && session->restrict_signal != signal_type) { + if (session->restrict_signal != DLEP_ALL_SIGNALS + && session->restrict_signal != DLEP_SESSION_TERMINATION_ACK + && session->restrict_signal != signal_type) { OONF_DEBUG(session->log_source, "Signal should have been %d," " drop session", @@ -433,7 +430,14 @@ dlep_session_process_signal(struct dlep_session *session, const void *ptr, size_ return -1; } - result = _process_tlvs(session, signal_type, signal_length, &buffer[4]); + if (session->restrict_signal == DLEP_SESSION_TERMINATION_ACK + && signal_type != DLEP_SESSION_TERMINATION_ACK) { + /* don't process other signals if we wait for a Termination Ack */ + result = DLEP_NEW_PARSER_OKAY; + } + else { + result = _process_tlvs(session, signal_type, signal_length, &buffer[4]); + } if (result == DLEP_NEW_PARSER_TERMINDATED) { /* session is now invalid, end parser */ @@ -834,6 +838,9 @@ _process_tlvs(struct dlep_session *session, int32_t signal_type, uint16_t signal static void _send_terminate(struct dlep_session *session, enum dlep_status status, const char *status_text) { if (session->restrict_signal != DLEP_UDP_PEER_DISCOVERY && session->restrict_signal != DLEP_UDP_PEER_OFFER) { + /* don't send anything else except for the Termination signal */ + abuf_clear(session->writer.out); + dlep_session_generate_signal_status(session, DLEP_SESSION_TERMINATION, NULL, status, status_text); session->restrict_signal = DLEP_SESSION_TERMINATION_ACK; @@ -1079,13 +1086,13 @@ _call_extension_processing(struct dlep_session *session, struct dlep_extension * if (session->radio) { if (ext->signals[s].process_radio && ext->signals[s].process_radio(ext, session)) { OONF_DEBUG(session->log_source, "Error in radio signal processing of extension '%s'", ext->name); - return -1; + return DLEP_NEW_PARSER_TERMINDATED; } } else { if (ext->signals[s].process_router && ext->signals[s].process_router(ext, session)) { OONF_DEBUG(session->log_source, "Error in router signal processing of extension '%s'", ext->name); - return -1; + return DLEP_NEW_PARSER_TERMINDATED; } } break; diff --git a/src/generic/dlep/dlep_telnet.c b/src/generic/dlep/dlep_telnet.c index 904ee568..4f51ee53 100644 --- a/src/generic/dlep/dlep_telnet.c +++ b/src/generic/dlep/dlep_telnet.c @@ -49,6 +49,8 @@ #include #include +#define SUBCOMMAND_TERMINATE "terminate" + #define KEY_IF_NAME "if_name" #define KEY_IF_SOCKET4 "if_socket4" #define KEY_IF_SOCKET6 "if_socket6" @@ -132,6 +134,28 @@ dlep_telnet_cleanup(void) { oonf_telnet_remove(&_dlep_cmd); } +/** + * Iterate over all DLEP sessions and terminate them + */ +static void +_terminate_all_dlep_sessions(void) { + struct dlep_if *interf; + struct dlep_session *session; + + avl_for_each_element(dlep_if_get_tree(true), interf, _node) { + avl_for_each_element(&interf->session_tree, session, _node) { + dlep_session_terminate(session, DLEP_STATUS_OKAY, + "DLEP session terminated by admin"); + } + } + avl_for_each_element(dlep_if_get_tree(false), interf, _node) { + avl_for_each_element(&interf->session_tree, session, _node) { + dlep_session_terminate(session, DLEP_STATUS_OKAY, + "DLEP session terminated by admin"); + } + } +} + /** * Callback for dlepinfo telnet command * @param con telnet connection @@ -139,6 +163,18 @@ dlep_telnet_cleanup(void) { */ static enum oonf_telnet_result _cb_dlepinfo_cmd(struct oonf_telnet_data *con) { + const char *next; + + if ((next = str_hasnextword(con->parameter, SUBCOMMAND_TERMINATE)) != NULL) { + if (strcasecmp(next, "true") == 0) { + _terminate_all_dlep_sessions(); + } + else { + abuf_puts(con->out, "Please use the additional boolean parameter 'true' to" + "terminate all DLEP sessions\n"); + return TELNET_RESULT_ACTIVE; + } + } return oonf_viewer_telnet_handler( con->out, &_template_storage, OONF_DLEP_SUBSYSTEM, con->parameter, _templates, ARRAYSIZE(_templates)); } @@ -150,8 +186,18 @@ _cb_dlepinfo_cmd(struct oonf_telnet_data *con) { */ static enum oonf_telnet_result _cb_dlepinfo_help(struct oonf_telnet_data *con) { - return oonf_viewer_telnet_help( - con->out, OONF_DLEP_SUBSYSTEM, con->parameter, _templates, ARRAYSIZE(_templates)); + enum oonf_telnet_result result; + + result = oonf_viewer_telnet_help( + con->out, OONF_DLEP_SUBSYSTEM, con->parameter, _templates, ARRAYSIZE(_templates); + if (result == TELNET_RESULT_ACTIVE) { + if (con->parameter == NULL || con->parameter[0] == 0 + || strcasecmp(con->parameter, SUBCOMMAND_TERMINATE) == 0) { + abuf_puts(con->out, SUBCOMMAND_TERMINATE + ": terminates all running dlep sessions\n"); + } + } + return result; } /** diff --git a/src/generic/dlep/ext_base_proto/proto.c b/src/generic/dlep/ext_base_proto/proto.c index 8d591111..42223b71 100644 --- a/src/generic/dlep/ext_base_proto/proto.c +++ b/src/generic/dlep/ext_base_proto/proto.c @@ -480,7 +480,14 @@ dlep_base_proto_process_session_termination( struct dlep_extension *ext __attribute__((unused)), struct dlep_session *session) { dlep_base_proto_print_status(session); + /* we can kill the session now */ session->_peer_state = DLEP_PEER_TERMINATED; + session->restrict_signal = DLEP_KILL_SESSION; + + /* ignore all signals we planned to send, only the Termination ACK is important now */ + abuf_clear(session->writer.out); + + /* generate Termination ACK */ return dlep_session_generate_signal(session, DLEP_SESSION_TERMINATION_ACK, NULL); } @@ -493,7 +500,12 @@ dlep_base_proto_process_session_termination( int dlep_base_proto_process_session_termination_ack( struct dlep_extension *ext __attribute__((unused)), struct dlep_session *session) { + /* we can kill the session now */ + session->_peer_state = DLEP_PEER_TERMINATED; session->restrict_signal = DLEP_KILL_SESSION; + + /* don't send anything anymore */ + abuf_clear(session->writer.out); return 0; } From 0d999885a784b84ad5b4c1e9c9aa1667cf1cfeea Mon Sep 17 00:00:00 2001 From: Henning Rogge Date: Mon, 17 Feb 2020 10:20:05 +0100 Subject: [PATCH 35/63] Fix dlep telnet source typo --- src/generic/dlep/dlep_telnet.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/generic/dlep/dlep_telnet.c b/src/generic/dlep/dlep_telnet.c index 4f51ee53..21be0605 100644 --- a/src/generic/dlep/dlep_telnet.c +++ b/src/generic/dlep/dlep_telnet.c @@ -189,7 +189,7 @@ _cb_dlepinfo_help(struct oonf_telnet_data *con) { enum oonf_telnet_result result; result = oonf_viewer_telnet_help( - con->out, OONF_DLEP_SUBSYSTEM, con->parameter, _templates, ARRAYSIZE(_templates); + con->out, OONF_DLEP_SUBSYSTEM, con->parameter, _templates, ARRAYSIZE(_templates)); if (result == TELNET_RESULT_ACTIVE) { if (con->parameter == NULL || con->parameter[0] == 0 || strcasecmp(con->parameter, SUBCOMMAND_TERMINATE) == 0) { From ae3580d051bc735f076a421f42e032daa0d6133b Mon Sep 17 00:00:00 2001 From: Henning Rogge Date: Fri, 3 Apr 2020 09:31:22 +0200 Subject: [PATCH 36/63] Fix layer2 access to network default data --- include/oonf/base/oonf_layer2.h | 4 ++-- src/generic/layer2info/layer2info.c | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/include/oonf/base/oonf_layer2.h b/include/oonf/base/oonf_layer2.h index 7b07c4cd..cf6e33f1 100644 --- a/include/oonf/base/oonf_layer2.h +++ b/include/oonf/base/oonf_layer2.h @@ -1121,8 +1121,8 @@ oonf_layer2_net_data_to_string( */ static INLINE const char * oonf_layer2_net_default_data_to_string( - char *buffer, size_t length, struct oonf_layer2_net *l2net, enum oonf_layer2_network_index idx, bool raw) { - return oonf_layer2_data_to_string(buffer, length, &l2net->data[idx], oonf_layer2_net_metadata_get(idx), raw); + char *buffer, size_t length, struct oonf_layer2_net *l2net, enum oonf_layer2_neighbor_index idx, bool raw) { + return oonf_layer2_data_to_string(buffer, length, &l2net->neighdata[idx], oonf_layer2_neigh_metadata_get(idx), raw); } diff --git a/src/generic/layer2info/layer2info.c b/src/generic/layer2info/layer2info.c index ab8a2cf7..818df389 100644 --- a/src/generic/layer2info/layer2info.c +++ b/src/generic/layer2info/layer2info.c @@ -533,10 +533,10 @@ static void _initialize_if_default_data_values(struct oonf_viewer_template *template, struct oonf_layer2_net *l2net) { size_t i; - memset(_value_if_data, 0, sizeof(_value_if_data)); + memset(_value_neigh_data, 0, sizeof(_value_neigh_data)); - for (i = 0; i < OONF_LAYER2_NET_COUNT; i++) { - oonf_layer2_net_default_data_to_string(_value_if_data[i], sizeof(_value_if_data[i]), l2net, i, template->create_raw); + for (i = 0; i < OONF_LAYER2_NEIGH_COUNT; i++) { + oonf_layer2_net_default_data_to_string(_value_neigh_data[i], sizeof(_value_neigh_data[i]), l2net, i, template->create_raw); } } From 2e20c850154a8e5ad052b517065a641de9ae6e59 Mon Sep 17 00:00:00 2001 From: Henning Rogge Date: Tue, 14 Apr 2020 08:18:08 +0200 Subject: [PATCH 37/63] Add support for DLEP rx/tx frame error rate TLV --- include/oonf/base/oonf_layer2.h | 10 +++- include/oonf/generic/dlep/dlep_iana.h | 50 +++++++++++-------- src/base/oonf_layer2.c | 4 +- .../dlep/ext_l2_statistics/l2_statistics.c | 26 ++++++++++ 4 files changed, 66 insertions(+), 24 deletions(-) diff --git a/include/oonf/base/oonf_layer2.h b/include/oonf/base/oonf_layer2.h index cf6e33f1..8b43f216 100644 --- a/include/oonf/base/oonf_layer2.h +++ b/include/oonf/base/oonf_layer2.h @@ -449,7 +449,7 @@ enum oonf_layer2_neighbor_index /*! incoming broadcast bitrate in bit/s */ OONF_LAYER2_NEIGH_RX_BC_BITRATE, - /*! incoming broadcast loss in 1/1000 */ + /*! incoming broadcast loss in 1/100 */ OONF_LAYER2_NEIGH_RX_BC_LOSS, /*! latency to neighbor in microseconds */ @@ -462,11 +462,17 @@ enum oonf_layer2_neighbor_index OONF_LAYER2_NEIGH_RADIO_HOPCOUNT, /*! - *IP hopcount (including ethernet between radio and router) to neighbor router, + * IP hopcount (including ethernet between radio and router) to neighbor router, * only available for multihop capable radios */ OONF_LAYER2_NEIGH_IP_HOPCOUNT, + /*! outgoing frame error rate in 1/100 */ + OONF_LAYER2_NEIGH_TX_FRAME_ERROR_RATE, + + /*! incoming frame error rate in 1/100 */ + OONF_LAYER2_NEIGH_RX_FRAME_ERROR_RATE, + /*! number of neighbor metrics */ OONF_LAYER2_NEIGH_COUNT, }; diff --git a/include/oonf/generic/dlep/dlep_iana.h b/include/oonf/generic/dlep/dlep_iana.h index bed50fe8..0287d8bc 100644 --- a/include/oonf/generic/dlep/dlep_iana.h +++ b/include/oonf/generic/dlep/dlep_iana.h @@ -279,73 +279,81 @@ enum dlep_tlvs DLEP_FREQUENCY_TLV = 65408, /*! channel bandwidth in Hz */ - DLEP_BANDWIDTH_TLV, + DLEP_BANDWIDTH_TLV = 65409, /*! noise level in milli dBm */ - DLEP_NOISE_LEVEL_TLV, + DLEP_NOISE_LEVEL_TLV = 65410, /*! total channel active time in ns */ - DLEP_CHANNEL_ACTIVE_TLV, + DLEP_CHANNEL_ACTIVE_TLV = 65411, /*! total channel busy time in ns */ - DLEP_CHANNEL_BUSY_TLV, + DLEP_CHANNEL_BUSY_TLV = 65412, /*! total channel receiver time in ns */ - DLEP_CHANNEL_RX_TLV, + DLEP_CHANNEL_RX_TLV = 65413, /*! total channel transmission time in ns */ - DLEP_CHANNEL_TX_TLV, + DLEP_CHANNEL_TX_TLV = 65414, /*! signal strength (receive) in milli dBm */ - DLEP_SIGNAL_RX_TLV, + DLEP_SIGNAL_RX_TLV = 65415, /*! signal strength (transmit) in milli dBm */ - DLEP_SIGNAL_TX_TLV, + DLEP_SIGNAL_TX_TLV = 65416, /* l2 statistics */ /*! total number of frames (receive) */ - DLEP_FRAMES_R_TLV, + DLEP_FRAMES_R_TLV = 65417, /*! total number of frames (transmit) */ - DLEP_FRAMES_T_TLV, + DLEP_FRAMES_T_TLV = 65418, /*! total number of bytes (receive) */ - DLEP_BYTES_R_TLV, + DLEP_BYTES_R_TLV = 65419, /*! total number of bytes (transmit) */ - DLEP_BYTES_T_TLV, + DLEP_BYTES_T_TLV = 65420, /*! outgoing throughput in bit/s */ - DLEP_THROUGHPUT_T_TLV, + DLEP_THROUGHPUT_T_TLV = 65421, /*! total number of frame retransmissions */ - DLEP_FRAMES_RETRIES_TLV, + DLEP_FRAMES_RETRIES_TLV = 65422, /*! total number of failed transmissions */ - DLEP_FRAMES_FAILED_TLV, + DLEP_FRAMES_FAILED_TLV = 65423, /* radio attributes */ /*! true if radio needs unicast traffic for MCS rate selection */ - DLEP_MCS_BY_PROBING, + DLEP_MCS_BY_PROBING = 65424, /*! true if radio can only receive unicast traffic */ - DLEP_RX_ONLY_UNICAST, + DLEP_RX_ONLY_UNICAST = 65425, /*! true if radio can only send unicast traffic */ - DLEP_TX_ONLY_UNICAST, + DLEP_TX_ONLY_UNICAST = 65426, /* more layer1 statistics */ /*! rx broadcast bitrate */ - DLEP_CDRR_BC_TLV, + DLEP_CDRR_BC_TLV = 65427, /* ipv4 DNS service */ - DLEP_IPV4_DNS_SERVER_TLV, + DLEP_IPV4_DNS_SERVER_TLV = 65428, /* ipv6 DNS service */ - DLEP_IPV6_DNS_SERVER_TLV, + DLEP_IPV6_DNS_SERVER_TLV = 65429, + + /* more layer 2 statistics */ + + /* current frame error rate in percent (receive) */ + DLEP_R_FRAME_ERROR_RATE_TLV = 65430, + + /* current frame error rate in percent (transmit) */ + DLEP_T_FRAME_ERROR_RATE_TLV = 65431, }; enum dlep_peer_type_flags diff --git a/src/base/oonf_layer2.c b/src/base/oonf_layer2.c index e003bf1b..629dbdf2 100644 --- a/src/base/oonf_layer2.c +++ b/src/base/oonf_layer2.c @@ -105,11 +105,13 @@ static const struct oonf_layer2_metadata _metadata_neigh[OONF_LAYER2_NEIGH_COUNT [OONF_LAYER2_NEIGH_TX_RLQ] = { .key = "tx_rlq", .type = OONF_LAYER2_INTEGER_DATA, .scaling = 1 }, [OONF_LAYER2_NEIGH_RX_RLQ] = { .key = "rx_rlq", .type = OONF_LAYER2_INTEGER_DATA, .scaling = 1 }, [OONF_LAYER2_NEIGH_RX_BC_BITRATE] = { .key = "rx_bc_bitrate", .type = OONF_LAYER2_INTEGER_DATA, .unit = "bit/s", .scaling = 1 }, - [OONF_LAYER2_NEIGH_RX_BC_LOSS] = { .key = "rx_bc_loss", .type = OONF_LAYER2_INTEGER_DATA, .scaling = 1000 }, + [OONF_LAYER2_NEIGH_RX_BC_LOSS] = { .key = "rx_bc_loss", .type = OONF_LAYER2_INTEGER_DATA, .scaling = 100 }, [OONF_LAYER2_NEIGH_LATENCY] = { .key = "latency", .type = OONF_LAYER2_INTEGER_DATA, .unit = "s", .scaling = 1000000 }, [OONF_LAYER2_NEIGH_RESOURCES] = { .key = "resources", .type = OONF_LAYER2_INTEGER_DATA, .scaling = 1 }, [OONF_LAYER2_NEIGH_RADIO_HOPCOUNT] = { .key = "radio_hopcount", .type = OONF_LAYER2_INTEGER_DATA, .scaling = 1 }, [OONF_LAYER2_NEIGH_IP_HOPCOUNT] = { .key = "ip_hopcount", .type = OONF_LAYER2_INTEGER_DATA, .scaling = 1 }, + [OONF_LAYER2_NEIGH_TX_FRAME_ERROR_RATE] = { .key = "tx_error_rate", .type = OONF_LAYER2_INTEGER_DATA, .scaling = 1 }, + [OONF_LAYER2_NEIGH_RX_FRAME_ERROR_RATE] = { .key = "rx_error_rate", .type = OONF_LAYER2_INTEGER_DATA, .scaling = 1 }, }; /* layer2 network metadata */ diff --git a/src/generic/dlep/ext_l2_statistics/l2_statistics.c b/src/generic/dlep/ext_l2_statistics/l2_statistics.c index 250476d1..e5b0781d 100644 --- a/src/generic/dlep/ext_l2_statistics/l2_statistics.c +++ b/src/generic/dlep/ext_l2_statistics/l2_statistics.c @@ -63,6 +63,8 @@ static const uint16_t _session_initack_tlvs[] = { DLEP_BYTES_T_TLV, DLEP_THROUGHPUT_T_TLV, DLEP_CDRR_BC_TLV, + DLEP_R_FRAME_ERROR_RATE_TLV, + DLEP_T_FRAME_ERROR_RATE_TLV, }; /* peer update */ @@ -75,6 +77,8 @@ static const uint16_t _peer_session_tlvs[] = { DLEP_BYTES_T_TLV, DLEP_THROUGHPUT_T_TLV, DLEP_CDRR_BC_TLV, + DLEP_R_FRAME_ERROR_RATE_TLV, + DLEP_T_FRAME_ERROR_RATE_TLV, }; /* destination up/update */ @@ -88,6 +92,8 @@ static const uint16_t _dst_tlvs[] = { DLEP_BYTES_T_TLV, DLEP_THROUGHPUT_T_TLV, DLEP_CDRR_BC_TLV, + DLEP_R_FRAME_ERROR_RATE_TLV, + DLEP_T_FRAME_ERROR_RATE_TLV, }; static const uint16_t _dst_mandatory[] = { DLEP_MAC_ADDRESS_TLV, @@ -139,6 +145,8 @@ static struct dlep_extension_tlv _tlvs[] = { { DLEP_BYTES_T_TLV, 8, 8 }, { DLEP_THROUGHPUT_T_TLV, 8, 8 }, { DLEP_CDRR_BC_TLV, 8, 8 }, + { DLEP_R_FRAME_ERROR_RATE_TLV, 1, 1 }, + { DLEP_T_FRAME_ERROR_RATE_TLV, 1, 1 }, }; static struct dlep_neighbor_mapping _neigh_mappings[] = { @@ -211,6 +219,24 @@ static struct dlep_neighbor_mapping _neigh_mappings[] = { .length = 8, .scaling = 1, + .from_tlv = dlep_reader_map_identity, + .to_tlv = dlep_writer_map_identity, + }, + { + .dlep = DLEP_R_FRAME_ERROR_RATE_TLV, + .layer2 = OONF_LAYER2_NEIGH_RX_FRAME_ERROR_RATE, + .length = 1, + .scaling = 1, + + .from_tlv = dlep_reader_map_identity, + .to_tlv = dlep_writer_map_identity, + }, + { + .dlep = DLEP_T_FRAME_ERROR_RATE_TLV, + .layer2 = OONF_LAYER2_NEIGH_TX_FRAME_ERROR_RATE, + .length = 8, + .scaling = 1, + .from_tlv = dlep_reader_map_identity, .to_tlv = dlep_writer_map_identity, }, From cca2727e60d048694ef450e09f32b2eb4464a97d Mon Sep 17 00:00:00 2001 From: Henning Rogge Date: Tue, 14 Apr 2020 09:29:56 +0200 Subject: [PATCH 38/63] Add partial DLEP code for hopcount extension, hopcontrol still missing --- include/oonf/generic/dlep/dlep_iana.h | 8 +- .../oonf/generic/dlep/ext_hopcount/hopcount.h | 55 ++++++ src/generic/dlep/CMakeLists.txt | 1 + src/generic/dlep/dlep.c | 2 + src/generic/dlep/ext_hopcount/hopcount.c | 157 ++++++++++++++++++ 5 files changed, 222 insertions(+), 1 deletion(-) create mode 100644 include/oonf/generic/dlep/ext_hopcount/hopcount.h create mode 100644 src/generic/dlep/ext_hopcount/hopcount.c diff --git a/include/oonf/generic/dlep/dlep_iana.h b/include/oonf/generic/dlep/dlep_iana.h index 0287d8bc..bb6d075b 100644 --- a/include/oonf/generic/dlep/dlep_iana.h +++ b/include/oonf/generic/dlep/dlep_iana.h @@ -93,6 +93,9 @@ enum dlep_extensions /*! DLEP metrics defined by base RFC */ DLEP_EXTENSION_BASE_METRIC = -1, + /*! RFC8629 */ + DLEP_EXTENSION_HOPCOUNT = 1, + /*! Additional DLEP physical layer statistics */ DLEP_EXTENSION_L1_STATS = 65520, @@ -109,7 +112,7 @@ enum dlep_extensions DLEP_EXTENSION_DNS = 65524, /*! number of supported (non-base) DLEP extensions */ - DLEP_EXTENSION_COUNT = 5, + DLEP_EXTENSION_COUNT = 6, }; /** @@ -354,6 +357,9 @@ enum dlep_tlvs /* current frame error rate in percent (transmit) */ DLEP_T_FRAME_ERROR_RATE_TLV = 65431, + + /* number of IP hops between routers attached to the radios */ + DLEP_IP_HOPCOUNT_TLV = 65432, }; enum dlep_peer_type_flags diff --git a/include/oonf/generic/dlep/ext_hopcount/hopcount.h b/include/oonf/generic/dlep/ext_hopcount/hopcount.h new file mode 100644 index 00000000..fc788c43 --- /dev/null +++ b/include/oonf/generic/dlep/ext_hopcount/hopcount.h @@ -0,0 +1,55 @@ + +/* + * The olsr.org Optimized Link-State Routing daemon version 2 (olsrd2) + * Copyright (c) 2004-2015, the olsr.org team - see HISTORY file + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of olsr.org, olsrd nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * Visit http://www.olsr.org for more information. + * + * If you find this software useful feel free to make a donation + * to the project. For more information see the website or contact + * the copyright holders. + * + */ + +/** + * @file + */ + +#ifndef _DLEP_HOPCOUNT_H_ +#define _DLEP_HOPCOUNT_H_ + +#include + +#include + +struct dlep_extension *dlep_hopcount_init(void); + +#endif /* _DLEP_HOPCOUNT_H_ */ diff --git a/src/generic/dlep/CMakeLists.txt b/src/generic/dlep/CMakeLists.txt index 2f533c5a..80c12f6f 100644 --- a/src/generic/dlep/CMakeLists.txt +++ b/src/generic/dlep/CMakeLists.txt @@ -4,6 +4,7 @@ SET (source ext_base_metric/metric.c ext_base_proto/proto.c ext_base_proto/proto_router.c ext_base_proto/proto_radio.c + ext_hopcount/hopcount.c ext_l1_statistics/l1_statistics.c ext_l2_statistics/l2_statistics.c ext_radio_attributes/radio_attributes.c diff --git a/src/generic/dlep/dlep.c b/src/generic/dlep/dlep.c index 1cc5e23d..b81388ee 100644 --- a/src/generic/dlep/dlep.c +++ b/src/generic/dlep/dlep.c @@ -64,6 +64,7 @@ #include #include #include +#include #include #include #include @@ -240,6 +241,7 @@ _init(void) { dlep_session_init(); dlep_base_ip_init(); dlep_base_metric_init(); + dlep_hopcount_init(); dlep_l1_statistics_init(); dlep_l2_statistics_init(); dlep_lid_init(); diff --git a/src/generic/dlep/ext_hopcount/hopcount.c b/src/generic/dlep/ext_hopcount/hopcount.c new file mode 100644 index 00000000..500467f9 --- /dev/null +++ b/src/generic/dlep/ext_hopcount/hopcount.c @@ -0,0 +1,157 @@ + +/* + * The olsr.org Optimized Link-State Routing daemon version 2 (olsrd2) + * Copyright (c) 2004-2015, the olsr.org team - see HISTORY file + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of olsr.org, olsrd nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * Visit http://www.olsr.org for more information. + * + * If you find this software useful feel free to make a donation + * to the project. For more information see the website or contact + * the copyright holders. + * + */ + +/** + * @file + */ + +#include +#include + +#include +#include +#include +#include + +#include + +/* peer initialization ack */ +static const uint16_t _session_initack_tlvs[] = { + DLEP_HOPCOUNT_TLV, +/* DLEP_HOPCONTROL_TLV, */ + DLEP_IP_HOPCOUNT_TLV, +}; + +/* peer update */ +static const uint16_t _peer_update_tlvs[] = { + DLEP_HOPCOUNT_TLV, +/* DLEP_HOPCONTROL_TLV, */ + DLEP_IP_HOPCOUNT_TLV, +}; + +/* destination up/update */ +static const uint16_t _dst_tlvs[] = { + DLEP_MAC_ADDRESS_TLV, + DLEP_HOPCOUNT_TLV, +/* DLEP_HOPCONTROL_TLV, */ + DLEP_IP_HOPCOUNT_TLV, +}; +static const uint16_t _dst_mandatory[] = { + DLEP_MAC_ADDRESS_TLV, +}; + +/* supported signals of this extension */ +static struct dlep_extension_signal _signals[] = { + { + .id = DLEP_SESSION_INITIALIZATION_ACK, + .supported_tlvs = _session_initack_tlvs, + .supported_tlv_count = ARRAYSIZE(_session_initack_tlvs), + .add_radio_tlvs = dlep_extension_radio_write_session_init_ack, + .process_router = dlep_extension_router_process_session_init_ack, + }, + { + .id = DLEP_SESSION_UPDATE, + .supported_tlvs = _peer_update_tlvs, + .supported_tlv_count = ARRAYSIZE(_peer_update_tlvs), + .add_radio_tlvs = dlep_extension_radio_write_session_update, + .process_router = dlep_extension_router_process_session_update, + }, + { + .id = DLEP_DESTINATION_UP, + .supported_tlvs = _dst_tlvs, + .supported_tlv_count = ARRAYSIZE(_dst_tlvs), + .mandatory_tlvs = _dst_mandatory, + .mandatory_tlv_count = ARRAYSIZE(_dst_mandatory), + .add_radio_tlvs = dlep_extension_radio_write_destination, + .process_router = dlep_extension_router_process_destination, + }, + { + .id = DLEP_DESTINATION_UPDATE, + .supported_tlvs = _dst_tlvs, + .supported_tlv_count = ARRAYSIZE(_dst_tlvs), + .mandatory_tlvs = _dst_mandatory, + .mandatory_tlv_count = ARRAYSIZE(_dst_mandatory), + .add_radio_tlvs = dlep_extension_radio_write_destination, + .process_router = dlep_extension_router_process_destination, + }, +}; + +/* supported TLVs of this extension */ +static struct dlep_extension_tlv _tlvs[] = { + { DLEP_HOPCOUNT_TLV, 2, 2 }, + { DLEP_HOPCONTROL_TLV, 2, 2 }, + { DLEP_IP_HOPCOUNT_TLV, 1, 1 }, +}; + +static struct dlep_neighbor_mapping _neigh_mappings[] = { + { + .dlep = DLEP_IP_HOPCOUNT_TLV, + .layer2 = OONF_LAYER2_NEIGH_IP_HOPCOUNT, + .length = 1, + .scaling = 1, + + .from_tlv = dlep_reader_map_identity, + .to_tlv = dlep_writer_map_identity, + }, +}; + +/* DLEP base extension, radio side */ +static struct dlep_extension _hopcount = { + .id = DLEP_EXTENSION_HOPCOUNT, + .name = "hopcount", + + .signals = _signals, + .signal_count = ARRAYSIZE(_signals), + .tlvs = _tlvs, + .tlv_count = ARRAYSIZE(_tlvs), + .neigh_mapping = _neigh_mappings, + .neigh_mapping_count = ARRAYSIZE(_neigh_mappings), +}; + +/** + * Get the layer1 statistics DLEP extension + * @return this extension + */ +struct dlep_extension * +dlep_hopcount_init(void) { + dlep_extension_add(&_hopcount); + return &_hopcount; +} From 4035f4fa0a2d6ebb1534ff33fa11834f53971b2a Mon Sep 17 00:00:00 2001 From: Henning Rogge Date: Fri, 24 Apr 2020 13:48:51 +0200 Subject: [PATCH 39/63] fix tx-error rate and add multihop capability to DLEP --- include/oonf/generic/dlep/dlep_iana.h | 3 +++ src/generic/dlep/ext_l2_statistics/l2_statistics.c | 2 +- .../dlep/ext_radio_attributes/radio_attributes.c | 10 ++++++++++ 3 files changed, 14 insertions(+), 1 deletion(-) diff --git a/include/oonf/generic/dlep/dlep_iana.h b/include/oonf/generic/dlep/dlep_iana.h index bb6d075b..428b951f 100644 --- a/include/oonf/generic/dlep/dlep_iana.h +++ b/include/oonf/generic/dlep/dlep_iana.h @@ -360,6 +360,9 @@ enum dlep_tlvs /* number of IP hops between routers attached to the radios */ DLEP_IP_HOPCOUNT_TLV = 65432, + + /* true if radio is able to forward traffic over multiple hops */ + DLEP_CAN_MULTIHOP = 65433, }; enum dlep_peer_type_flags diff --git a/src/generic/dlep/ext_l2_statistics/l2_statistics.c b/src/generic/dlep/ext_l2_statistics/l2_statistics.c index e5b0781d..aa5f94ab 100644 --- a/src/generic/dlep/ext_l2_statistics/l2_statistics.c +++ b/src/generic/dlep/ext_l2_statistics/l2_statistics.c @@ -234,7 +234,7 @@ static struct dlep_neighbor_mapping _neigh_mappings[] = { { .dlep = DLEP_T_FRAME_ERROR_RATE_TLV, .layer2 = OONF_LAYER2_NEIGH_TX_FRAME_ERROR_RATE, - .length = 8, + .length = 1, .scaling = 1, .from_tlv = dlep_reader_map_identity, diff --git a/src/generic/dlep/ext_radio_attributes/radio_attributes.c b/src/generic/dlep/ext_radio_attributes/radio_attributes.c index 9d6fcdff..827d2b48 100644 --- a/src/generic/dlep/ext_radio_attributes/radio_attributes.c +++ b/src/generic/dlep/ext_radio_attributes/radio_attributes.c @@ -58,6 +58,7 @@ static const uint16_t _session_initack_tlvs[] = { DLEP_MCS_BY_PROBING, DLEP_RX_ONLY_UNICAST, DLEP_TX_ONLY_UNICAST, + DLEP_CAN_MULTIHOP, }; /* peer update */ @@ -65,6 +66,7 @@ static const uint16_t _peer_session_tlvs[] = { DLEP_MCS_BY_PROBING, DLEP_RX_ONLY_UNICAST, DLEP_TX_ONLY_UNICAST, + DLEP_CAN_MULTIHOP, }; /* supported signals of this extension */ @@ -90,6 +92,7 @@ static struct dlep_extension_tlv _tlvs[] = { { DLEP_MCS_BY_PROBING, 1, 1 }, { DLEP_RX_ONLY_UNICAST, 1, 1 }, { DLEP_TX_ONLY_UNICAST, 1, 1 }, + { DLEP_CAN_MULTIHOP, 1, 1 }, }; static struct dlep_network_mapping _net_mappings[] = { @@ -109,6 +112,13 @@ static struct dlep_network_mapping _net_mappings[] = { }, { .dlep = DLEP_TX_ONLY_UNICAST, + .layer2 = OONF_LAYER2_NET_RADIO_MULTIHOP, + .length = 1, + .from_tlv = dlep_reader_map_identity, + .to_tlv = dlep_writer_map_identity, + }, + { + .dlep = DLEP_CAN_MULTIHOP, .layer2 = OONF_LAYER2_NET_TX_ONLY_UNICAST, .length = 1, .from_tlv = dlep_reader_map_identity, From 664fadd8a1a5a6c35a34c034d8bc55bb38a21c67 Mon Sep 17 00:00:00 2001 From: Henning Rogge Date: Tue, 19 May 2020 11:02:34 +0200 Subject: [PATCH 40/63] Add packet error rate used size to DLEP/layer2 --- include/oonf/base/oonf_layer2.h | 6 + src/base/oonf_layer2.c | 2 + .../dlep/ext_l1_statistics/l1_statistics.c | 2 + .../dlep/ext_l2_statistics/l2_statistics.c | 126 ++++++++++++++++-- 4 files changed, 128 insertions(+), 8 deletions(-) diff --git a/include/oonf/base/oonf_layer2.h b/include/oonf/base/oonf_layer2.h index 8b43f216..84a0b8b8 100644 --- a/include/oonf/base/oonf_layer2.h +++ b/include/oonf/base/oonf_layer2.h @@ -473,6 +473,12 @@ enum oonf_layer2_neighbor_index /*! incoming frame error rate in 1/100 */ OONF_LAYER2_NEIGH_RX_FRAME_ERROR_RATE, + /*! outgoing frame error rate used packet size in bytes */ + OONF_LAYER2_NEIGH_TX_FRAME_ERROR_RATE_PKTSIZE, + + /*! incoming frame error rate used packet size in bytes */ + OONF_LAYER2_NEIGH_RX_FRAME_ERROR_RATE_PKTSIZE, + /*! number of neighbor metrics */ OONF_LAYER2_NEIGH_COUNT, }; diff --git a/src/base/oonf_layer2.c b/src/base/oonf_layer2.c index 629dbdf2..e9d2a68e 100644 --- a/src/base/oonf_layer2.c +++ b/src/base/oonf_layer2.c @@ -112,6 +112,8 @@ static const struct oonf_layer2_metadata _metadata_neigh[OONF_LAYER2_NEIGH_COUNT [OONF_LAYER2_NEIGH_IP_HOPCOUNT] = { .key = "ip_hopcount", .type = OONF_LAYER2_INTEGER_DATA, .scaling = 1 }, [OONF_LAYER2_NEIGH_TX_FRAME_ERROR_RATE] = { .key = "tx_error_rate", .type = OONF_LAYER2_INTEGER_DATA, .scaling = 1 }, [OONF_LAYER2_NEIGH_RX_FRAME_ERROR_RATE] = { .key = "rx_error_rate", .type = OONF_LAYER2_INTEGER_DATA, .scaling = 1 }, + [OONF_LAYER2_NEIGH_TX_FRAME_ERROR_RATE_PKTSIZE] = { .key = "tx_error_pktsize", .type = OONF_LAYER2_INTEGER_DATA, .scaling = 1 }, + [OONF_LAYER2_NEIGH_RX_FRAME_ERROR_RATE_PKTSIZE] = { .key = "rx_error_pktsize", .type = OONF_LAYER2_INTEGER_DATA, .scaling = 1 }, }; /* layer2 network metadata */ diff --git a/src/generic/dlep/ext_l1_statistics/l1_statistics.c b/src/generic/dlep/ext_l1_statistics/l1_statistics.c index ace65e1a..7b6479d2 100644 --- a/src/generic/dlep/ext_l1_statistics/l1_statistics.c +++ b/src/generic/dlep/ext_l1_statistics/l1_statistics.c @@ -316,9 +316,11 @@ _reader_map_array (struct oonf_layer2_data *data, const struct oonf_layer2_metad switch (l2idx) { case OONF_LAYER2_NET_BANDWIDTH_1: data += (OONF_LAYER2_NET_BANDWIDTH_2 - OONF_LAYER2_NET_BANDWIDTH_1); + meta += (OONF_LAYER2_NET_BANDWIDTH_2 - OONF_LAYER2_NET_BANDWIDTH_1); break; case OONF_LAYER2_NET_FREQUENCY_1: data += (OONF_LAYER2_NET_FREQUENCY_2 - OONF_LAYER2_NET_FREQUENCY_1); + meta += (OONF_LAYER2_NET_FREQUENCY_2 - OONF_LAYER2_NET_FREQUENCY_1); break; default: return -1; diff --git a/src/generic/dlep/ext_l2_statistics/l2_statistics.c b/src/generic/dlep/ext_l2_statistics/l2_statistics.c index aa5f94ab..5acfef31 100644 --- a/src/generic/dlep/ext_l2_statistics/l2_statistics.c +++ b/src/generic/dlep/ext_l2_statistics/l2_statistics.c @@ -53,6 +53,13 @@ #include +static int _reader_error_rate( + struct oonf_layer2_data *data, const struct oonf_layer2_metadata *meta, + struct dlep_session *session, uint16_t dlep_tlv, uint64_t scaling); +static int _writer_error_rate( + struct dlep_writer *writer, struct oonf_layer2_data *data, + const struct oonf_layer2_metadata *meta, uint16_t tlv, uint16_t length, uint64_t scaling); + /* peer initialization ack */ static const uint16_t _session_initack_tlvs[] = { DLEP_FRAMES_R_TLV, @@ -145,8 +152,8 @@ static struct dlep_extension_tlv _tlvs[] = { { DLEP_BYTES_T_TLV, 8, 8 }, { DLEP_THROUGHPUT_T_TLV, 8, 8 }, { DLEP_CDRR_BC_TLV, 8, 8 }, - { DLEP_R_FRAME_ERROR_RATE_TLV, 1, 1 }, - { DLEP_T_FRAME_ERROR_RATE_TLV, 1, 1 }, + { DLEP_R_FRAME_ERROR_RATE_TLV, 3, 3 }, + { DLEP_T_FRAME_ERROR_RATE_TLV, 3, 3 }, }; static struct dlep_neighbor_mapping _neigh_mappings[] = { @@ -225,20 +232,20 @@ static struct dlep_neighbor_mapping _neigh_mappings[] = { { .dlep = DLEP_R_FRAME_ERROR_RATE_TLV, .layer2 = OONF_LAYER2_NEIGH_RX_FRAME_ERROR_RATE, - .length = 1, + .length = 3, .scaling = 1, - .from_tlv = dlep_reader_map_identity, - .to_tlv = dlep_writer_map_identity, + .from_tlv = _reader_error_rate, + .to_tlv = _writer_error_rate, }, { .dlep = DLEP_T_FRAME_ERROR_RATE_TLV, .layer2 = OONF_LAYER2_NEIGH_TX_FRAME_ERROR_RATE, - .length = 1, + .length = 3, .scaling = 1, - .from_tlv = dlep_reader_map_identity, - .to_tlv = dlep_writer_map_identity, + .from_tlv = _reader_error_rate, + .to_tlv = _writer_error_rate, }, }; @@ -264,3 +271,106 @@ dlep_l2_statistics_init(void) { dlep_extension_add(&_l2_stats); return &_l2_stats; } + +/** + * Read frame error rate TLV into layer2 database objects + * @param data layer2 network data array + * @param meta metadata description for data + * @param session dlep session + * @param dlep_tlv dlep TLV id + * @param scaling fixed integer arithmetics scaling factor + * @return -1 if an error happened, 0 otherwise + */ +static int +_reader_error_rate(struct oonf_layer2_data *data, const struct oonf_layer2_metadata *meta, + struct dlep_session *session, uint16_t dlep_tlv, uint64_t scaling) { + struct dlep_parser_value *value; + const uint8_t *dlepvalue; + uint8_t error_rate; + uint16_t pkt_size; + + value = dlep_session_get_tlv_value(session, dlep_tlv); + if (!value) { + return 0; + } + if (scaling != 1 || value->length != 3) { + return -1; + } + + /* read binary values */ + dlepvalue = dlep_parser_get_tlv_binary(&session->parser, value); + memcpy(&error_rate, dlepvalue, 1); + memcpy(&pkt_size, &dlepvalue[1], 2); + pkt_size = ntohs(pkt_size); + + /* set error rate */ + oonf_layer2_data_set_int64(data, session->l2_origin, meta, error_rate, scaling); + + switch (dlep_tlv) { + case DLEP_R_FRAME_ERROR_RATE_TLV: + data += (OONF_LAYER2_NEIGH_RX_FRAME_ERROR_RATE_PKTSIZE - OONF_LAYER2_NEIGH_RX_FRAME_ERROR_RATE); + meta += (OONF_LAYER2_NEIGH_RX_FRAME_ERROR_RATE_PKTSIZE - OONF_LAYER2_NEIGH_RX_FRAME_ERROR_RATE); + break; + case DLEP_T_FRAME_ERROR_RATE_TLV: + data += (OONF_LAYER2_NEIGH_TX_FRAME_ERROR_RATE_PKTSIZE - OONF_LAYER2_NEIGH_TX_FRAME_ERROR_RATE); + meta += (OONF_LAYER2_NEIGH_TX_FRAME_ERROR_RATE_PKTSIZE - OONF_LAYER2_NEIGH_TX_FRAME_ERROR_RATE); + break; + default: + return -1; + } + + /* set error rate pktsize */ + oonf_layer2_data_set_int64(data, session->l2_origin, meta, pkt_size, scaling); + return 0; +} + +/** + * Map layer2 frequency to DLEP TLV + * @param writer dlep writer + * @param data layer2 network data array + * @param tlv DLEP tlv id + * @param length tlv length + * @param scaling fixed integer arithmetics scaling factor + * @return -1 if an error happened, 0 otherwise + */ +static int +_writer_error_rate (struct dlep_writer *writer, struct oonf_layer2_data *data, + const struct oonf_layer2_metadata *meta, uint16_t tlv, uint16_t length, uint64_t scaling) { + struct oonf_layer2_data *data2; + int64_t l2value; + uint64_t tmp16; + uint8_t dlep_value[3]; + + if (scaling != 1 || length != 3) { + return -1; + } + if (meta->type != OONF_LAYER2_INTEGER_DATA) { + return -1; + } + + switch (tlv) { + case DLEP_R_FRAME_ERROR_RATE_TLV: + data2 = data + (OONF_LAYER2_NEIGH_RX_FRAME_ERROR_RATE_PKTSIZE - OONF_LAYER2_NEIGH_RX_FRAME_ERROR_RATE); + break; + case DLEP_T_FRAME_ERROR_RATE_TLV: + data2 = data + (OONF_LAYER2_NEIGH_TX_FRAME_ERROR_RATE_PKTSIZE - OONF_LAYER2_NEIGH_TX_FRAME_ERROR_RATE); + break; + default: + return -1; + } + + if (oonf_layer2_data_read_int64(&l2value, data, scaling)) { + return 0; + } + dlep_value[0] = l2value; + + if (oonf_layer2_data_read_int64(&l2value, data2, scaling)) { + return 0; + } + tmp16 = l2value; + tmp16 = htons(tmp16); + memcpy(&dlep_value[1], &tmp16, 2); + + dlep_writer_add_tlv(writer, tlv, &dlep_value[0], length); + return 0; +} From 3c5c7ad58ed524a487744cc0c7f712ca816c3cb6 Mon Sep 17 00:00:00 2001 From: Henning Rogge Date: Wed, 24 Jun 2020 15:48:59 +0200 Subject: [PATCH 41/63] Add logging to olsrv2-l2import --- include/oonf/base/os_routing.h | 13 +++++++++ src/olsrv2/olsrv2_l2import/olsrv2_l2import.c | 30 +++++++++++++++++++- 2 files changed, 42 insertions(+), 1 deletion(-) diff --git a/include/oonf/base/os_routing.h b/include/oonf/base/os_routing.h index 8cb08b7c..c8f36e98 100644 --- a/include/oonf/base/os_routing.h +++ b/include/oonf/base/os_routing.h @@ -266,6 +266,19 @@ EXPORT int os_routing_avl_cmp_route_key(const void *, const void *); EXPORT const char *os_routing_cfg_get_rttype(size_t index, const void *unused); +/** + * Convert route key into string + * @param buf target string buffer + * @param key route key + */ +static INLINE const char * +os_routing_key_to_string(struct os_route_str *buf, const struct os_route_key *key) { + struct netaddr_str buf1, buf2; + buf->buf[0] = 0; + snprintf(buf->buf, sizeof(*buf), "[dst=%s/src=%s]", netaddr_to_string(&buf1, &key->dst), netaddr_to_string(&buf2, &key->src)); + return buf->buf; +} + /* include os-specific headers */ #if defined(__linux__) #include diff --git a/src/olsrv2/olsrv2_l2import/olsrv2_l2import.c b/src/olsrv2/olsrv2_l2import/olsrv2_l2import.c index 75fc3990..ab848adb 100644 --- a/src/olsrv2/olsrv2_l2import/olsrv2_l2import.c +++ b/src/olsrv2/olsrv2_l2import/olsrv2_l2import.c @@ -200,6 +200,7 @@ _get_l2export(const char *name) { mod->_node.key = mod->originator; avl_insert(&_l2export_tree, &mod->_node); + OONF_DEBUG(LOG_L2IMPORT, "Adding import-origin '%s'", name); return mod; } @@ -226,6 +227,8 @@ _destroy_l2export(struct _l2export_data *l2import) { } } + OONF_DEBUG(LOG_L2IMPORT, "Removing import-origin '%s'", l2import->originator); + // TODO: iterate over the l2 database if we need to remove something from the olsrv2 LAN database oonf_class_free(&_l2export_class, l2import); } @@ -233,17 +236,24 @@ _destroy_l2export(struct _l2export_data *l2import) { static bool _is_matching_origin(struct oonf_layer2_neighbor_address *addr, const char *pattern) { int len; + bool result; if (strcmp(addr->origin->name, pattern) == 0) { + OONF_DEBUG(LOG_L2IMPORT, "Exact origin match: '%s'", addr->origin->name); + return true; } len = strlen(pattern); if (len == 0 || pattern[len-1] != '*') { + OONF_DEBUG(LOG_L2IMPORT, "No origin match: '%s' != '%s'", addr->origin->name, pattern); return false; } - return strncmp(addr->origin->name, pattern, len-1) == 0; + result = strncmp(addr->origin->name, pattern, len-1) == 0; + OONF_DEBUG(LOG_L2IMPORT, "Origin pattern cmp: '%s' %s '%s'", + addr->origin->name, result ? "==" : "!=", pattern); + return result; } static void @@ -252,15 +262,25 @@ _remove_l2neighip_lans(struct oonf_layer2_neighbor_address *nip) { struct os_route_key rt_key; struct nhdp_domain *domain; +#ifdef OONF_LOG_DEBUG_INFO + struct os_route_str rbuf; os_routing_init_sourcespec_prefix(&rt_key, &nip->ip); +#endif avl_for_each_element(&_l2export_tree, l2import, _node) { if (_is_matching_origin(nip, l2import->originator)) { if (l2import->domain >= 0) { domain = nhdp_domain_get_by_ext(l2import->domain); + + OONF_DEBUG(LOG_L2IMPORT, "Remove lan (%d): %s", l2import->domain, + os_routing_key_to_string(&rbuf, &rt_key)); + olsrv2_lan_remove(domain, &rt_key); } else { + OONF_DEBUG(LOG_L2IMPORT, "Remove lan (all): %s", + os_routing_key_to_string(&rbuf, &rt_key)); + list_for_each_element(nhdp_domain_get_list(), domain, _node) { olsrv2_lan_remove(domain, &rt_key); } @@ -277,6 +297,10 @@ _cb_l2neigh_ip_added(void *ptr) { struct nhdp_domain *domain; uint32_t metric; int32_t distance; + +#ifdef OONF_LOG_DEBUG_INFO + struct os_route_str rbuf; +#endif os_routing_init_sourcespec_prefix(&rt_key, &nip->ip); avl_for_each_element(&_l2export_tree, l2import, _node) { @@ -293,9 +317,13 @@ _cb_l2neigh_ip_added(void *ptr) { metric = l2import->routing_metric; } + OONF_DEBUG(LOG_L2IMPORT, "Add lan (%d): %s", l2import->domain, + os_routing_key_to_string(&rbuf, &rt_key)); olsrv2_lan_add(domain, &rt_key, metric , distance); } else { + OONF_DEBUG(LOG_L2IMPORT, "Add lan (all): %s", + os_routing_key_to_string(&rbuf, &rt_key)); list_for_each_element(nhdp_domain_get_list(), domain, _node) { metric = 1; if (l2import->routing_metric < RFC7181_METRIC_MIN) { From 67dd7999cd33655301ae088cc56168ccbb71aee9 Mon Sep 17 00:00:00 2001 From: Henning Rogge Date: Thu, 25 Jun 2020 08:51:59 +0200 Subject: [PATCH 42/63] Sync olsrd2-dlep app plugins with olsrd2 app --- apps/olsrd2-dlep/CMakeLists.txt | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/apps/olsrd2-dlep/CMakeLists.txt b/apps/olsrd2-dlep/CMakeLists.txt index 894b6f86..36d780a0 100644 --- a/apps/olsrd2-dlep/CMakeLists.txt +++ b/apps/olsrd2-dlep/CMakeLists.txt @@ -55,17 +55,24 @@ IF (NOT OONF_STATIC_PLUGINS) olsrv2 olsrv2info olsrv2_lan + olsrv2_old_lan + olsrv2_l2import netjsoninfo layer2_import auto_ll4 http + mpr + remotecontrol + route_modifier dlep ) ENDIF (NOT OONF_STATIC_PLUGINS) IF (NOT OONF_OPTIONAL_STATIC_PLUGINS) - set (OONF_OPTIONAL_STATIC_PLUGINS nl80211_listener) + set (OONF_OPTIONAL_STATIC_PLUGINS nl80211_listener + cfg_uciloader + ) ENDIF (NOT OONF_OPTIONAL_STATIC_PLUGINS) ################################## From 8127855450b035e48226a1bd30f281afd8b4aaa5 Mon Sep 17 00:00:00 2001 From: Henning Rogge Date: Thu, 13 Aug 2020 09:44:48 +0200 Subject: [PATCH 43/63] Allow routing tables up to 65535 --- src/olsrv2/olsrv2/olsrv2.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/olsrv2/olsrv2/olsrv2.c b/src/olsrv2/olsrv2/olsrv2.c index c041d0f4..8530cc98 100644 --- a/src/olsrv2/olsrv2/olsrv2.c +++ b/src/olsrv2/olsrv2/olsrv2.c @@ -160,7 +160,7 @@ static struct cfg_schema_entry _rt_domain_entries[] = { "Set the source IP of IPv4-routes to a fixed value."), CFG_MAP_INT32_MINMAX( olsrv2_routing_domain, protocol, "protocol", "100", "Protocol number to be used in routing table", 0, 1, 254), - CFG_MAP_INT32_MINMAX(olsrv2_routing_domain, table, "table", "254", "Routing table number for routes", 0, 1, 254), + CFG_MAP_INT32_MINMAX(olsrv2_routing_domain, table, "table", "254", "Routing table number for routes", 0, 1, 65535), CFG_MAP_INT32_MINMAX( olsrv2_routing_domain, distance, "distance", "2", "Metric Distance to be used in routing table", 0, 1, 255), CFG_MAP_BOOL( From 927a77d5ab4410aef97b13fadcc154c90b490f83 Mon Sep 17 00:00:00 2001 From: Henning Rogge Date: Thu, 13 Aug 2020 10:40:24 +0200 Subject: [PATCH 44/63] Add support for 32 bit routing tables in netlink code --- include/oonf/base/os_routing.h | 2 +- src/base/os_linux/os_routing_linux.c | 9 ++++++++- src/generic/layer2_import/layer2_import.c | 2 +- 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/include/oonf/base/os_routing.h b/include/oonf/base/os_routing.h index c8f36e98..cf94989a 100644 --- a/include/oonf/base/os_routing.h +++ b/include/oonf/base/os_routing.h @@ -189,7 +189,7 @@ struct os_route_parameter { int metric; /*! routing table protocol */ - unsigned char table; + uint32_t table; /*! routing routing protocol */ unsigned char protocol; diff --git a/src/base/os_linux/os_routing_linux.c b/src/base/os_linux/os_routing_linux.c index 18de9c8e..89195e35 100644 --- a/src/base/os_linux/os_routing_linux.c +++ b/src/base/os_linux/os_routing_linux.c @@ -408,7 +408,11 @@ _routing_set(struct os_system_netlink_message *nl_msg, struct os_route *route, u rt_msg->rtm_family = route->p.family; rt_msg->rtm_scope = rt_scope; rt_msg->rtm_protocol = route->p.protocol; - rt_msg->rtm_table = route->p.table; + + /* add routing table */ + if (os_system_linux_netlink_addreq(nl_msg, RTA_TABLE, &route->p.table, sizeof(route->p.table))) { + return -1; + } /* set default route type */ rt_msg->rtm_type = RTN_UNICAST; @@ -536,6 +540,9 @@ _routing_parse_nlmsg(struct os_route *route, struct nlmsghdr *msg) { netaddr_from_binary_prefix( &route->p.key.src, RTA_DATA(rt_attr), RTA_PAYLOAD(rt_attr), rt_msg->rtm_family, rt_msg->rtm_src_len); break; + case RTA_TABLE: + memcpy(&route->p.table, RTA_DATA(rt_attr), sizeof(route->p.table)); + break; case RTA_PRIORITY: memcpy(&route->p.metric, RTA_DATA(rt_attr), sizeof(route->p.metric)); break; diff --git a/src/generic/layer2_import/layer2_import.c b/src/generic/layer2_import/layer2_import.c index b9255f2e..c4b555ae 100644 --- a/src/generic/layer2_import/layer2_import.c +++ b/src/generic/layer2_import/layer2_import.c @@ -424,7 +424,7 @@ _cb_rt_event(const struct os_route *route, bool set) { } /* check routing table */ - if (import->table != -1 && import->table != route->p.table) { + if (import->table != -1 && (uint32_t)(import->table) != route->p.table) { OONF_DEBUG(LOG_L2_IMPORT, "Bad routing table %u (filter was %d)", route->p.table, import->table); continue; } From 61ed1de3a6a2d8d97be0e4311d332d5a020bbc98 Mon Sep 17 00:00:00 2001 From: Henning Rogge Date: Thu, 27 Aug 2020 10:43:00 +0200 Subject: [PATCH 45/63] Add type 'all' to lan-import --- include/oonf/base/os_routing.h | 1 + src/base/os_generic/os_routing_generic_rt_to_string.c | 1 + src/base/os_linux/os_routing_linux.c | 5 +++++ src/generic/layer2_import/layer2_import.c | 6 +++--- src/libconfig/cfg_validate.c | 2 +- 5 files changed, 11 insertions(+), 4 deletions(-) diff --git a/include/oonf/base/os_routing.h b/include/oonf/base/os_routing.h index cf94989a..a93a8d74 100644 --- a/include/oonf/base/os_routing.h +++ b/include/oonf/base/os_routing.h @@ -131,6 +131,7 @@ enum os_route_type OS_ROUTE_PROHIBIT, OS_ROUTE_BLACKHOLE, OS_ROUTE_NAT, + OS_ROUTE_ALL, OS_ROUTE_COUNT }; diff --git a/src/base/os_generic/os_routing_generic_rt_to_string.c b/src/base/os_generic/os_routing_generic_rt_to_string.c index 2bdd274c..f5cf1fcc 100644 --- a/src/base/os_generic/os_routing_generic_rt_to_string.c +++ b/src/base/os_generic/os_routing_generic_rt_to_string.c @@ -58,6 +58,7 @@ static const char *_route_types[] = { [OS_ROUTE_PROHIBIT] = "prohibit", [OS_ROUTE_BLACKHOLE] = "blackhole", [OS_ROUTE_NAT] = "nat", + [OS_ROUTE_ALL] = "all", }; /** diff --git a/src/base/os_linux/os_routing_linux.c b/src/base/os_linux/os_routing_linux.c index 89195e35..bc187cdf 100644 --- a/src/base/os_linux/os_routing_linux.c +++ b/src/base/os_linux/os_routing_linux.c @@ -398,6 +398,11 @@ _routing_set(struct os_system_netlink_message *nl_msg, struct os_route *route, u route->p.family = netaddr_get_address_family(&route->p.src_ip); } + if (route->p.type == OS_ROUTE_ALL) { + /* cannot set meta-type 'route_all' */ + return -1; + } + if (route->p.family == AF_UNSPEC) { route->p.family = AF_INET; } diff --git a/src/generic/layer2_import/layer2_import.c b/src/generic/layer2_import/layer2_import.c index c4b555ae..f8777558 100644 --- a/src/generic/layer2_import/layer2_import.c +++ b/src/generic/layer2_import/layer2_import.c @@ -147,7 +147,7 @@ static struct cfg_schema_entry _l2_entries[] = { CFG_MAP_STRING_ARRAY( _import_entry, ifname, "interface", "", "Interface name of matching routes, empty if all interfaces", IF_NAMESIZE), CFG_MAP_INT32_MINMAX( - _import_entry, table, "table", "-1", "Routing table of matching routes, 0 for matching all tables", 0, -1, 255), + _import_entry, table, "table", "-1", "Routing table of matching routes, 0 for matching all tables", 0, -1, 65535), CFG_MAP_INT32_MINMAX( _import_entry, protocol, "protocol", "-1", "Routing protocol of matching routes, 0 for all protocols", 0, -1, 255), CFG_MAP_INT32_MINMAX( @@ -173,7 +173,7 @@ static struct cfg_schema_entry _lan_entries[] = { CFG_MAP_STRING_ARRAY( _import_entry, ifname, "interface", "", "Interface name of matching routes, empty if all interfaces", IF_NAMESIZE), CFG_MAP_INT32_MINMAX( - _import_entry, table, "table", "-1", "Routing table of matching routes, 0 for matching all tables", 0, -1, 255), + _import_entry, table, "table", "-1", "Routing table of matching routes, 0 for matching all tables", 0, -1, 65535), CFG_MAP_INT32_MINMAX( _import_entry, protocol, "protocol", "-1", "Routing protocol of matching routes, 0 for all protocols", 0, -1, 255), CFG_MAP_INT32_MINMAX( @@ -405,7 +405,7 @@ _cb_rt_event(const struct os_route *route, bool set) { avl_for_each_element(&_import_tree, import, _node) { OONF_DEBUG(LOG_L2_IMPORT, "Check for import: %s", import->name); - if (import->rttype != route->p.type) { + if (import->rttype != OS_ROUTE_ALL && import->rttype != route->p.type) { OONF_DEBUG(LOG_L2_IMPORT, "Bad routing type %u (filter was %d)", route->p.type, import->rttype); continue; diff --git a/src/libconfig/cfg_validate.c b/src/libconfig/cfg_validate.c index f2bf0510..e478f164 100644 --- a/src/libconfig/cfg_validate.c +++ b/src/libconfig/cfg_validate.c @@ -189,7 +189,7 @@ cfg_validate_int(struct autobuf *out, const char *section_name, const char *entr cfg_append_printable_line(out, "Value '%s' for entry '%s' in section %s is " "larger than %s", - value, entry_name, section_name, isonumber_from_s64(&hbuf, min, "", scaling, true)); + value, entry_name, section_name, isonumber_from_s64(&hbuf, max, "", scaling, true)); return -1; } return 0; From 253d4aa4e2125115226d42808fd94360a8e491d1 Mon Sep 17 00:00:00 2001 From: Henning Rogge Date: Thu, 27 Aug 2020 11:35:14 +0200 Subject: [PATCH 46/63] Return exit code zero if the user uses --quit and nothing goes wrong --- src/libcore/oonf_main.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/libcore/oonf_main.c b/src/libcore/oonf_main.c index 412063af..21532f52 100644 --- a/src/libcore/oonf_main.c +++ b/src/libcore/oonf_main.c @@ -77,7 +77,7 @@ static void parse_early_commandline(int argc, char **argv); static int parse_commandline(int argc, char **argv, const struct oonf_appdata *, bool reload_only); static int display_schema(void); -static bool _end_oonf_signal, _display_schema, _debug_early, _ignore_unknown; +static bool _display_schema, _debug_early, _ignore_unknown; static char *_schema_name; static int (*_handle_scheduling)(void) = NULL; @@ -163,7 +163,6 @@ oonf_main(int argc, char **argv, const struct oonf_appdata *appdata) { _ignore_unknown = false; /* setup signal handler */ - _end_oonf_signal = false; setup_signalhandler(); /* parse "early" command line arguments */ @@ -247,10 +246,10 @@ oonf_main(int argc, char **argv, const struct oonf_appdata *appdata) { if (!oonf_cfg_is_running()) { /* - * mayor error during late initialization - * or maybe the user decided otherwise and pressed CTRL-C + * maybe the user decided otherwise and pressed CTRL-C + * or the --quit option was used */ - return_code = _end_oonf_signal ? 0 : 1; + return_code = 0; goto oonf_cleanup; } From d678d3f13ba498f9571ec215b33ea65415569b94 Mon Sep 17 00:00:00 2001 From: Henning Rogge Date: Thu, 3 Sep 2020 09:33:04 +0200 Subject: [PATCH 47/63] Add olsrv2info local telnet command --- src/olsrv2/olsrv2info/olsrv2info.c | 73 +++++++++++++++++++++++++++--- 1 file changed, 67 insertions(+), 6 deletions(-) diff --git a/src/olsrv2/olsrv2info/olsrv2info.c b/src/olsrv2/olsrv2info/olsrv2info.c index e9f34c4f..1dde4983 100644 --- a/src/olsrv2/olsrv2info/olsrv2info.c +++ b/src/olsrv2/olsrv2info/olsrv2info.c @@ -84,6 +84,7 @@ static void _initialize_attached_network_values(struct olsrv2_tc_attachment *edg static void _initialize_edge_values(struct olsrv2_tc_edge *edge); static void _initialize_route_values(struct olsrv2_routing_entry *route); +static int _cb_create_text_local(struct oonf_viewer_template *); static int _cb_create_text_originator(struct oonf_viewer_template *); static int _cb_create_text_old_originator(struct oonf_viewer_template *); static int _cb_create_text_lan(struct oonf_viewer_template *); @@ -98,6 +99,21 @@ static int _cb_create_text_route(struct oonf_viewer_template *); * The keys are API, so they should not be changed after published */ +/*! template key for local ANSN */ +#define KEY_LOCAL_ANSN "local_ansn" + +/*! template key for local TC interval time */ +#define KEY_LOCAL_ITIME "local_itime" + +/*! template key for local TC validity time */ +#define KEY_LOCAL_VTIME "local_vtime" + +/*! template key for local IPv4 originator */ +#define KEY_LOCAL_ORIG_V4 "local_orig4" + +/*! template key for local IPv6 originator */ +#define KEY_LOCAL_ORIG_V6 "local_orig6" + /*! template key for originator IP */ #define KEY_ORIGINATOR "originator" @@ -201,6 +217,12 @@ static int _cb_create_text_route(struct oonf_viewer_template *); * buffer space for values that will be assembled * into the output of the plugin */ +static char _value_local_ansn[6]; +static struct isonumber_str _value_local_itime; +static struct isonumber_str _value_local_vtime; +static struct netaddr_str _value_local_orig4; +static struct netaddr_str _value_local_orig6; + static struct netaddr_str _value_originator; static struct netaddr_str _value_old_originator; @@ -241,6 +263,14 @@ static char _value_route_ifindex[12]; static struct netaddr_str _value_route_lasthop; /* definition of the template data entries for JSON and table output */ +static struct abuf_template_data_entry _tde_local[] = { + { KEY_LOCAL_ANSN, _value_local_ansn, true }, + { KEY_LOCAL_ITIME, _value_local_itime.buf, false }, + { KEY_LOCAL_VTIME, _value_local_vtime.buf, false }, + { KEY_LOCAL_ORIG_V4, _value_local_orig4.buf, true }, + { KEY_LOCAL_ORIG_V6, _value_local_orig6.buf, true } +}; + static struct abuf_template_data_entry _tde_originator[] = { { KEY_ORIGINATOR, _value_originator.buf, true }, }; @@ -312,6 +342,9 @@ static struct abuf_template_data_entry _tde_route[] = { static struct abuf_template_storage _template_storage; /* Template Data objects (contain one or more Template Data Entries) */ +static struct abuf_template_data _td_local[] = { + { _tde_local, ARRAYSIZE(_tde_local) }, +}; static struct abuf_template_data _td_orig[] = { { _tde_originator, ARRAYSIZE(_tde_originator) }, }; @@ -348,12 +381,19 @@ static struct abuf_template_data _td_route[] = { }; /* OONF viewer templates (based on Template Data arrays) */ -static struct oonf_viewer_template _templates[] = { { - .data = _td_orig, - .data_size = ARRAYSIZE(_td_orig), - .json_name = "originator", - .cb_function = _cb_create_text_originator, - }, +static struct oonf_viewer_template _templates[] = { + { + .data = _td_local, + .data_size = ARRAYSIZE(_td_local), + .json_name = "local", + .cb_function = _cb_create_text_local, + }, + { + .data = _td_orig, + .data_size = ARRAYSIZE(_td_orig), + .json_name = "originator", + .cb_function = _cb_create_text_originator, + }, { .data = _td_old_orig, .data_size = ARRAYSIZE(_td_old_orig), @@ -612,6 +652,27 @@ _cb_create_text_old_originator(struct oonf_viewer_template *template) { return 0; } +/** + * Display the local olsrv2 settings + * @param template oonf viewer template + * @return -1 if an error happened, 0 otherwise + */ +static int +_cb_create_text_local(struct oonf_viewer_template *template) { + /* generate template output */ + snprintf(_value_local_ansn, sizeof(_value_local_ansn), "%u", olsrv2_routing_get_ansn()); + + oonf_clock_toIntervalString_ext(&_value_local_itime, olsrv2_get_tc_interval(), template->create_raw); + oonf_clock_toIntervalString_ext(&_value_local_vtime, olsrv2_get_tc_validity(), template->create_raw); + + netaddr_to_string(&_value_local_orig4, olsrv2_originator_get(AF_INET)); + netaddr_to_string(&_value_local_orig6, olsrv2_originator_get(AF_INET6)); + + oonf_viewer_output_print_line(template); + return 0; +} + + /** * Display the originator addresses of the local node * @param template oonf viewer template From 4f8ae01d9aebaef4da936d7346ba244bec6e2b56 Mon Sep 17 00:00:00 2001 From: Henning Rogge Date: Thu, 3 Sep 2020 13:44:30 +0200 Subject: [PATCH 48/63] Fix triggering dijkstra when receiving LAN updates --- src/olsrv2/olsrv2/olsrv2_reader.c | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/src/olsrv2/olsrv2/olsrv2_reader.c b/src/olsrv2/olsrv2/olsrv2_reader.c index 73498e26..1d8c80f5 100644 --- a/src/olsrv2/olsrv2/olsrv2_reader.c +++ b/src/olsrv2/olsrv2/olsrv2_reader.c @@ -450,6 +450,7 @@ _handle_gateways(struct rfc5444_reader_tlvblock_entry *tlv, struct os_route_key struct olsrv2_tc_attachment *end; struct nhdp_domain *domain; size_t i; + bool cleanup[NHDP_MAXIMUM_DOMAINS]; /* check length */ if (tlv->length > 1 && tlv->length < _current.mprtypes_size) { @@ -488,11 +489,9 @@ _handle_gateways(struct rfc5444_reader_tlvblock_entry *tlv, struct os_route_key end->ansn = _current.node->ansn; - if (_current.complete_tc) { - /* clear unused metrics */ - for (i = 0; i < NHDP_MAXIMUM_DOMAINS; i++) { - end->cost[i] = RFC7181_METRIC_INFINITE; - } + /* clear unused metrics */ + for (i = 0; i < NHDP_MAXIMUM_DOMAINS; i++) { + cleanup[i] = end->cost[i] != RFC7181_METRIC_INFINITE; } /* use MT definition of AN tlv */ @@ -503,7 +502,12 @@ _handle_gateways(struct rfc5444_reader_tlvblock_entry *tlv, struct os_route_key continue; } - end->cost[domain->index] = cost_out[domain->index]; + cleanup[domain->index] = false; + + if (end->cost[domain->index] != cost_out[domain->index]) { + _current.changed[domain->index] = true; + end->cost[domain->index] = cost_out[domain->index]; + } if (tlv->length == 1) { end->distance[domain->index] = tlv->single_value[0]; @@ -515,6 +519,13 @@ _handle_gateways(struct rfc5444_reader_tlvblock_entry *tlv, struct os_route_key OONF_DEBUG( LOG_OLSRV2_R, "Address is Attached Network (domain %u): dist=%u", domain->ext, end->distance[domain->index]); } + + for (i = 0; i < NHDP_MAXIMUM_DOMAINS; i++) { + if (cleanup[i]) { + end->cost[i] = RFC7181_METRIC_INFINITE; + _current.changed[i] = true; + } + } } /** From e5758c541631f96f6b66cbf292c234d4b0955553 Mon Sep 17 00:00:00 2001 From: Henning Rogge Date: Mon, 7 Sep 2020 15:08:41 +0200 Subject: [PATCH 49/63] Allow import routing tables with a block of a specific protocol --- src/generic/layer2_import/layer2_import.c | 22 ++++++++++++++------ src/olsrv2/olsrv2_l2import/olsrv2_l2import.c | 4 ++-- 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/src/generic/layer2_import/layer2_import.c b/src/generic/layer2_import/layer2_import.c index f8777558..8be316f0 100644 --- a/src/generic/layer2_import/layer2_import.c +++ b/src/generic/layer2_import/layer2_import.c @@ -90,13 +90,16 @@ struct _import_entry { /*! filter by interface name, length null to ignore*/ char ifname[IF_NAMESIZE]; - /*! filter by routing table id, 0 to ignore */ + /*! filter by routing table id, -1 to ignore */ int32_t table; - /*! filter by routing protocol id, 0 to ignore */ + /*! filter by routing protocol id, -1 to ignore */ int32_t protocol; - /*! filter by routing metric, 0 to ignore */ + /*! block certain routing protocol id, -1 to ignore */ + int32_t block_protocol; + + /*! filter by routing metric, -1 to ignore */ int32_t distance; /*! routing type to be imported, nearly always unicast */ @@ -147,11 +150,14 @@ static struct cfg_schema_entry _l2_entries[] = { CFG_MAP_STRING_ARRAY( _import_entry, ifname, "interface", "", "Interface name of matching routes, empty if all interfaces", IF_NAMESIZE), CFG_MAP_INT32_MINMAX( - _import_entry, table, "table", "-1", "Routing table of matching routes, 0 for matching all tables", 0, -1, 65535), + _import_entry, table, "table", "-1", "Routing table of matching routes, -1 for matching all tables", 0, -1, 65535), CFG_MAP_INT32_MINMAX( - _import_entry, protocol, "protocol", "-1", "Routing protocol of matching routes, 0 for all protocols", 0, -1, 255), + _import_entry, protocol, "protocol", "-1", "Routing protocol of matching routes, -1 for all protocols", 0, -1, 255), CFG_MAP_INT32_MINMAX( - _import_entry, distance, "metric", "-1", "Metric of matching routes, 0 for all metrics", 0, -1, INT32_MAX), + _import_entry, block_protocol, "block_protocol", "-1", + "Routing protocol not imported, -1 to not block any protocol", 0, -1, 255), + CFG_MAP_INT32_MINMAX( + _import_entry, distance, "metric", "-1", "Metric of matching routes, -1 for all metrics", 0, -1, INT32_MAX), CFG_MAP_OS_ROUTING_TYPE_KEY( _import_entry, rttype, "rttype", "unicast", "Type of routing metric to be imported"), CFG_MAP_STRING_ARRAY(_import_entry, fixed_mac_if, "fixed_mac_if", "", @@ -434,6 +440,10 @@ _cb_rt_event(const struct os_route *route, bool set) { OONF_DEBUG(LOG_L2_IMPORT, "Bad protocol %u (filter was %d)", route->p.protocol, import->protocol); continue; } + if (set && import->block_protocol != -1 && import->block_protocol == route->p.protocol) { + OONF_DEBUG(LOG_L2_IMPORT, "Bad protocol %u (block was %d)", route->p.protocol, import->protocol); + continue; + } /* check metric */ if (import->distance != -1 && import->distance != route->p.metric) { diff --git a/src/olsrv2/olsrv2_l2import/olsrv2_l2import.c b/src/olsrv2/olsrv2_l2import/olsrv2_l2import.c index ab848adb..7df2a1ff 100644 --- a/src/olsrv2/olsrv2_l2import/olsrv2_l2import.c +++ b/src/olsrv2/olsrv2_l2import/olsrv2_l2import.c @@ -97,11 +97,11 @@ static void _cb_cfg_changed(void); static struct cfg_schema_entry _l2import_entries[] = { CFG_MAP_INT32_MINMAX(_l2export_data, domain, "domain", "-1", - "domain for the imported LAN entries, -1 for all domains", 0, -1, 255), + "domain for the imported LAN entries, -1 for all domains", 0, -1, NHDP_MAXIMUM_DOMAINS), CFG_MAP_INT32_MINMAX(_l2export_data, routing_metric, "metric", "-1", "routing metric for the imported LAN entries, -1 to calculate from layer2 data", 0, -1, RFC7181_METRIC_MAX), CFG_MAP_INT32_MINMAX(_l2export_data, fib_distance, "fib_distance", "2", - "fib distance for imported LAN entries, -1 for all domains", 0, 1, 255), + "fib distance for imported LAN entries", 0, 1, 255), }; static struct cfg_schema_section _l2import_section = { From 49abf5690bb2488767575fb58d5b3b1f154f21e6 Mon Sep 17 00:00:00 2001 From: Henning Rogge Date: Mon, 7 Sep 2020 15:37:52 +0200 Subject: [PATCH 50/63] Add block_protocol to lan_import --- src/generic/layer2_import/layer2_import.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/generic/layer2_import/layer2_import.c b/src/generic/layer2_import/layer2_import.c index 8be316f0..eadb8c4b 100644 --- a/src/generic/layer2_import/layer2_import.c +++ b/src/generic/layer2_import/layer2_import.c @@ -179,11 +179,14 @@ static struct cfg_schema_entry _lan_entries[] = { CFG_MAP_STRING_ARRAY( _import_entry, ifname, "interface", "", "Interface name of matching routes, empty if all interfaces", IF_NAMESIZE), CFG_MAP_INT32_MINMAX( - _import_entry, table, "table", "-1", "Routing table of matching routes, 0 for matching all tables", 0, -1, 65535), + _import_entry, table, "table", "-1", "Routing table of matching routes, -1 for matching all tables", 0, -1, 65535), + CFG_MAP_INT32_MINMAX( + _import_entry, protocol, "protocol", "-1", "Routing protocol of matching routes, -1 for all protocols", 0, -1, 255), CFG_MAP_INT32_MINMAX( - _import_entry, protocol, "protocol", "-1", "Routing protocol of matching routes, 0 for all protocols", 0, -1, 255), + _import_entry, block_protocol, "block_protocol", "-1", + "Routing protocol not imported, -1 to not block any protocol", 0, -1, 255), CFG_MAP_INT32_MINMAX( - _import_entry, distance, "metric", "-1", "Metric of matching routes, 0 for all metrics", 0, -1, INT32_MAX), + _import_entry, distance, "metric", "-1", "Metric of matching routes, -1 for all metrics", 0, -1, INT32_MAX), CFG_MAP_OS_ROUTING_TYPE_KEY( _import_entry, rttype, "rttype", "unicast", "Type of routing metric to be imported"), CFG_MAP_STRING_ARRAY(_import_entry, fixed_mac_if, "fixed_mac_if", "", From 815b99d8b9bacfd6b59e7ef18e5a77f285b6ca55 Mon Sep 17 00:00:00 2001 From: Henning Rogge Date: Tue, 8 Sep 2020 12:46:26 +0200 Subject: [PATCH 51/63] Add l2net import feature --- src/generic/layer2_import/layer2_import.c | 118 +++++++++++++--------- 1 file changed, 72 insertions(+), 46 deletions(-) diff --git a/src/generic/layer2_import/layer2_import.c b/src/generic/layer2_import/layer2_import.c index eadb8c4b..e8f34d4d 100644 --- a/src/generic/layer2_import/layer2_import.c +++ b/src/generic/layer2_import/layer2_import.c @@ -105,6 +105,9 @@ struct _import_entry { /*! routing type to be imported, nearly always unicast */ enum os_route_type rttype; + /*! true if online routes are put into interface_ip db (and non-onlink are ignored) */ + bool l2net; + /*! set MAC address of imported entries to this interface */ char fixed_mac_if[IF_NAMESIZE]; @@ -160,6 +163,7 @@ static struct cfg_schema_entry _l2_entries[] = { _import_entry, distance, "metric", "-1", "Metric of matching routes, -1 for all metrics", 0, -1, INT32_MAX), CFG_MAP_OS_ROUTING_TYPE_KEY( _import_entry, rttype, "rttype", "unicast", "Type of routing metric to be imported"), + CFG_MAP_BOOL(_import_entry, l2net, "l2net", "false", "Put imported onlink routes into interface_ip database"), CFG_MAP_STRING_ARRAY(_import_entry, fixed_mac_if, "fixed_mac_if", "", "Name of interface that will be used to fill in layer2 entry MAC addresses", IF_NAMESIZE), CFG_MAP_STRING_ARRAY(_import_entry, fixed_l2if_name, "fixed_l2if_name", "", @@ -385,6 +389,7 @@ _cb_rt_event(const struct os_route *route, bool set) { char ifname[IF_NAMESIZE]; struct oonf_layer2_net *l2net; struct oonf_layer2_neigh *l2neigh; + struct oonf_layer2_peer_address *l2local; struct oonf_layer2_neighbor_address *l2neigh_ip; struct oonf_layer2_neigh_key nb_key; const struct netaddr *gw, *dst, *mac; @@ -414,6 +419,13 @@ _cb_rt_event(const struct os_route *route, bool set) { avl_for_each_element(&_import_tree, import, _node) { OONF_DEBUG(LOG_L2_IMPORT, "Check for import: %s", import->name); + /* ignore "offlink" routes for l2net import */ + if (import->l2net && !netaddr_is_unspec(&route->p.gw)) { + OONF_DEBUG(LOG_L2_IMPORT, "Route was not onlink"); + continue; + } + + /* check type of route */ if (import->rttype != OS_ROUTE_ALL && import->rttype != route->p.type) { OONF_DEBUG(LOG_L2_IMPORT, "Bad routing type %u (filter was %d)", route->p.type, import->rttype); @@ -493,60 +505,74 @@ _cb_rt_event(const struct os_route *route, bool set) { continue; } - mac = NULL; - macifname = ""; - if (import->fixed_mac_if[0]) { - if (import->fixed_if_listener.data) { - mac = &import->fixed_if_listener.data->mac; - macifname = import->fixed_if_listener.data->name; + dst = &route->p.key.dst; + + if (import->l2net) { + l2local = oonf_layer2_net_get_local_ip(l2net, dst); + if (set && l2local == NULL) { + OONF_DEBUG(LOG_L2_IMPORT, "Set new l2net prefix: %s", netaddr_to_string(&nbuf, dst)); + oonf_layer2_net_add_ip(l2net, &import->l2origin, dst); } - } - else { - mac = &l2net->if_listener.data->mac; - macifname = l2net->if_listener.data->name; - } - if (netaddr_is_unspec(mac)) { - OONF_DEBUG(LOG_L2_IMPORT, "Wait for interface (%s) data to be initialized", macifname); - if (!oonf_timer_is_active(&_route_reload_instance)) { - oonf_timer_set(&_route_reload_instance, 1000); + else if (!set && l2local != NULL) { + OONF_DEBUG(LOG_L2_IMPORT, "Remove l2net prefix: %s", netaddr_to_string(&nbuf, dst)); + oonf_layer2_net_remove_ip(l2local, &import->l2origin); } - continue; } - - dst = &route->p.key.dst; - gw = &route->p.gw; - - l2neigh_ip = _remove_old_entries(l2net, import, gw, dst); - l2neigh = NULL; - /* get layer2 neighbor */ - if (set && !l2neigh_ip) { - /* generate l2 key including LID */ - if (oonf_layer2_neigh_generate_lid(&nb_key, &import->l2origin, mac)) { - OONF_WARN(LOG_L2_IMPORT, "Could not generate LID for MAC %s (if %s)", - netaddr_to_string(&nbuf, mac), macifname); - continue; + else{ + mac = NULL; + macifname = ""; + if (import->fixed_mac_if[0]) { + if (import->fixed_if_listener.data) { + mac = &import->fixed_if_listener.data->mac; + macifname = import->fixed_if_listener.data->name; + } } - - l2neigh = oonf_layer2_neigh_add_lid(l2net, &nb_key); - if (!l2neigh) { - OONF_DEBUG(LOG_L2_IMPORT, "No l2 neighbor found"); + else { + mac = &l2net->if_listener.data->mac; + macifname = l2net->if_listener.data->name; + } + if (netaddr_is_unspec(mac)) { + OONF_DEBUG(LOG_L2_IMPORT, "Wait for interface (%s) data to be initialized", macifname); + if (!oonf_timer_is_active(&_route_reload_instance)) { + oonf_timer_set(&_route_reload_instance, 1000); + } continue; } - OONF_DEBUG(LOG_L2_IMPORT, "Import layer2 neighbor..."); - - /* make sure next hop is initialized */ - oonf_layer2_neigh_set_nexthop(l2neigh, gw); - if (!oonf_layer2_neigh_get_remote_ip(l2neigh, dst)) { - oonf_layer2_neigh_add_ip(l2neigh, &import->l2origin, dst); + gw = &route->p.gw; + + l2neigh_ip = _remove_old_entries(l2net, import, gw, dst); + l2neigh = NULL; + /* get layer2 neighbor */ + if (set && !l2neigh_ip) { + /* generate l2 key including LID */ + if (oonf_layer2_neigh_generate_lid(&nb_key, &import->l2origin, mac)) { + OONF_WARN(LOG_L2_IMPORT, "Could not generate LID for MAC %s (if %s)", + netaddr_to_string(&nbuf, mac), macifname); + continue; + } + + l2neigh = oonf_layer2_neigh_add_lid(l2net, &nb_key); + if (!l2neigh) { + OONF_DEBUG(LOG_L2_IMPORT, "No l2 neighbor found"); + continue; + } + + OONF_DEBUG(LOG_L2_IMPORT, "Import layer2 neighbor..."); + + /* make sure next hop is initialized */ + oonf_layer2_neigh_set_nexthop(l2neigh, gw); + if (!oonf_layer2_neigh_get_remote_ip(l2neigh, dst)) { + oonf_layer2_neigh_add_ip(l2neigh, &import->l2origin, dst); + } + oonf_layer2_neigh_commit(l2neigh); + } + else if (!set && l2neigh_ip) { + l2neigh = l2neigh_ip->l2neigh; + oonf_layer2_neigh_remove_ip(l2neigh_ip, &import->l2origin); + oonf_layer2_neigh_commit(l2neigh); + } } - oonf_layer2_neigh_commit(l2neigh); - } - else if (!set && l2neigh_ip) { - l2neigh = l2neigh_ip->l2neigh; - oonf_layer2_neigh_remove_ip(l2neigh_ip, &import->l2origin); - oonf_layer2_neigh_commit(l2neigh); - } } } From c7798692afc07b88dd94f91026247d842bf537dc Mon Sep 17 00:00:00 2001 From: Henning Rogge Date: Mon, 14 Sep 2020 10:23:39 +0200 Subject: [PATCH 52/63] Remove layer2 data on DLEP session teardown --- src/generic/dlep/dlep_session.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/generic/dlep/dlep_session.c b/src/generic/dlep/dlep_session.c index 56479721..6e40b899 100644 --- a/src/generic/dlep/dlep_session.c +++ b/src/generic/dlep/dlep_session.c @@ -184,6 +184,7 @@ void dlep_session_remove(struct dlep_session *session) { struct dlep_parser_tlv *tlv, *tlv_it; struct dlep_session_parser *parser; + struct oonf_layer2_net *l2net; #ifdef OONF_LOG_DEBUG_INFO struct netaddr_str nbuf; #endif @@ -194,6 +195,13 @@ dlep_session_remove(struct dlep_session *session) { netaddr_socket_invalidate(&session->local_socket); netaddr_socket_invalidate(&session->remote_socket); + /* cleanup values set by DLEP */ + l2net = oonf_layer2_net_get(session->l2_listener.name); + if (l2net) { + oonf_layer2_net_cleanup(l2net, session->l2_origin, true); + oonf_layer2_net_cleanup(l2net, session->l2_default_origin, true); + } + os_interface_remove(&session->l2_listener); parser = &session->parser; From b55f36d2a69c84cae1cd59eefde999d1f2378cbb Mon Sep 17 00:00:00 2001 From: Henning Rogge Date: Wed, 23 Sep 2020 11:37:31 +0200 Subject: [PATCH 53/63] Add json based import/export plugin for layer2 library --- include/oonf/base/oonf_clock.h | 2 +- include/oonf/base/oonf_layer2.h | 13 + .../oonf/generic/layer2_json/layer2_json.h | 52 ++ .../layer2_json/layer2_json_internal.h | 59 ++ include/oonf/libcommon/isonumber.h | 4 +- src/base/oonf_layer2.c | 19 +- src/generic/CMakeLists.txt | 1 + src/generic/layer2_json/CMakeLists.txt | 17 + src/generic/layer2_json/l2json_export.c | 351 +++++++++++ src/generic/layer2_json/l2json_import.c | 547 ++++++++++++++++++ src/generic/layer2_json/layer2json.c | 140 +++++ src/generic/link_config/link_config.c | 2 +- src/libcommon/isonumber.c | 109 +++- src/libconfig/cfg_tobin.c | 2 +- src/libconfig/cfg_validate.c | 2 +- src/tests/common/test_common_isonumber.c | 37 +- 16 files changed, 1316 insertions(+), 41 deletions(-) create mode 100644 include/oonf/generic/layer2_json/layer2_json.h create mode 100644 include/oonf/generic/layer2_json/layer2_json_internal.h create mode 100644 src/generic/layer2_json/CMakeLists.txt create mode 100644 src/generic/layer2_json/l2json_export.c create mode 100644 src/generic/layer2_json/l2json_import.c create mode 100644 src/generic/layer2_json/layer2json.c diff --git a/include/oonf/base/oonf_clock.h b/include/oonf/base/oonf_clock.h index d76a0894..262570fd 100644 --- a/include/oonf/base/oonf_clock.h +++ b/include/oonf/base/oonf_clock.h @@ -212,7 +212,7 @@ oonf_clock_fromIntervalString(uint64_t *result, const char *string) { int64_t t; int r; - r = isonumber_to_s64(&t, string, 1000); + r = isonumber_to_s64(&t, string, NULL, 1000); if (r == 0) { *result = t; } diff --git a/include/oonf/base/oonf_layer2.h b/include/oonf/base/oonf_layer2.h index 84a0b8b8..dd1011a3 100644 --- a/include/oonf/base/oonf_layer2.h +++ b/include/oonf/base/oonf_layer2.h @@ -718,6 +718,7 @@ EXPORT void oonf_layer2_help_mac_lid(const struct cfg_schema_entry *entry, struc EXPORT int oonf_layer2_tobin_mac_lid(const struct cfg_schema_entry *s_entry, const struct const_strarray *value, void *reference); EXPORT const char *oonf_layer2_net_get_type_name(enum oonf_layer2_network_type); +EXPORT enum oonf_layer2_network_type oonf_layer2_get_type(const char *name); EXPORT struct avl_tree *oonf_layer2_get_net_tree(void); EXPORT struct avl_tree *oonf_layer2_get_origin_tree(void); @@ -736,6 +737,18 @@ oonf_layer2_origin_is_added(const struct oonf_layer2_origin *origin) { return avl_is_node_added(&origin->_node); } +/** + * Get a layer-2 origin object from the database + * @param ifname name of origin + * @return origin object, NULL if not found + */ +static INLINE struct oonf_layer2_origin * +oonf_layer2_origin_get(const char *name) { + struct oonf_layer2_origin *l2origin; + return avl_find_element(oonf_layer2_get_origin_tree(), name, l2origin, _node); +} + + /** * Get a layer-2 interface object from the database * @param ifname name of interface diff --git a/include/oonf/generic/layer2_json/layer2_json.h b/include/oonf/generic/layer2_json/layer2_json.h new file mode 100644 index 00000000..e359757b --- /dev/null +++ b/include/oonf/generic/layer2_json/layer2_json.h @@ -0,0 +1,52 @@ + +/* + * The olsr.org Optimized Link-State Routing daemon version 2 (olsrd2) + * Copyright (c) 2004-2015, the olsr.org team - see HISTORY file + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of olsr.org, olsrd nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * Visit http://www.olsr.org for more information. + * + * If you find this software useful feel free to make a donation + * to the project. For more information see the website or contact + * the copyright holders. + * + */ + +/** + * @file + */ + +#ifndef LAYER2JSON_H_ +#define LAYER2JSON_H_ + +/*! subsystem identifier */ +#define OONF_LAYER2JSON_SUBSYSTEM "layer2_json" + +#endif /* LAYER2JSON_H_ */ diff --git a/include/oonf/generic/layer2_json/layer2_json_internal.h b/include/oonf/generic/layer2_json/layer2_json_internal.h new file mode 100644 index 00000000..8b4c21c5 --- /dev/null +++ b/include/oonf/generic/layer2_json/layer2_json_internal.h @@ -0,0 +1,59 @@ + +/* + * The olsr.org Optimized Link-State Routing daemon version 2 (olsrd2) + * Copyright (c) 2004-2015, the olsr.org team - see HISTORY file + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of olsr.org, olsrd nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * Visit http://www.olsr.org for more information. + * + * If you find this software useful feel free to make a donation + * to the project. For more information see the website or contact + * the copyright holders. + * + */ + +/** + * @file + */ + +#ifndef LAYER2JSON_INTERNAL_H_ +#define LAYER2JSON_INTERNAL_H_ + +#include +#include + +int l2json_export(struct oonf_telnet_data *con); +int l2json_import(struct oonf_telnet_data *con, const char *input); +void l2json_import_init(enum oonf_log_source log); +void l2json_import_cleanup(void); +void l2json_export_init(enum oonf_log_source log); +void l2json_export_cleanup(void); + +#endif /* LAYER2JSON_INTERNAL_H_ */ diff --git a/include/oonf/libcommon/isonumber.h b/include/oonf/libcommon/isonumber.h index d3dd95f8..e28e4fa1 100644 --- a/include/oonf/libcommon/isonumber.h +++ b/include/oonf/libcommon/isonumber.h @@ -61,7 +61,7 @@ EXPORT const char *isonumber_from_u64( struct isonumber_str *out, uint64_t number, const char *unit, uint64_t scaling, bool raw); EXPORT const char *isonumber_from_s64( struct isonumber_str *out, int64_t number, const char *unit, uint64_t scaling, bool raw); -EXPORT int isonumber_to_u64(uint64_t *dst, const char *iso, uint64_t scaling); -EXPORT int isonumber_to_s64(int64_t *dst, const char *iso, uint64_t scaling); +EXPORT int isonumber_to_u64(uint64_t *dst, const char *iso, const char *unit, uint64_t scaling); +EXPORT int isonumber_to_s64(int64_t *dst, const char *iso, const char *unit, uint64_t scaling); #endif /* ISONUMBER_H_ */ diff --git a/src/base/oonf_layer2.c b/src/base/oonf_layer2.c index e9d2a68e..0c1cd181 100644 --- a/src/base/oonf_layer2.c +++ b/src/base/oonf_layer2.c @@ -296,7 +296,7 @@ oonf_layer2_data_parse_string( switch (meta->type) { case OONF_LAYER2_INTEGER_DATA: - return isonumber_to_s64(&value->integer, input, meta->scaling); + return isonumber_to_s64(&value->integer, input, meta->unit, meta->scaling); case OONF_LAYER2_BOOLEAN_DATA: if (!cfg_is_bool(input)) { @@ -1434,6 +1434,23 @@ oonf_layer2_net_get_type_name(enum oonf_layer2_network_type type) { return _network_type[type]; } +/** + * get network type from string + * @param name type name + * @return oonf_layer2_network_type, undefined if unknown string + */ +enum oonf_layer2_network_type +oonf_layer2_get_type(const char *name) { + enum oonf_layer2_network_type type; + + for (type = 0; type < OONF_LAYER2_TYPE_COUNT; type++) { + if (strcmp(_network_type[type], name) == 0) { + return type; + } + } + return OONF_LAYER2_TYPE_UNDEFINED; +} + /** * get tree of layer2 networks * @return network tree diff --git a/src/generic/CMakeLists.txt b/src/generic/CMakeLists.txt index 99edc348..9d085f8b 100644 --- a/src/generic/CMakeLists.txt +++ b/src/generic/CMakeLists.txt @@ -9,6 +9,7 @@ add_subdirectory(layer2_import) add_subdirectory(layer2_config) add_subdirectory(layer2_export) add_subdirectory(layer2_generator) +add_subdirectory(layer2_json) add_subdirectory(link_config) add_subdirectory(plugin_controller) add_subdirectory(remotecontrol) diff --git a/src/generic/layer2_json/CMakeLists.txt b/src/generic/layer2_json/CMakeLists.txt new file mode 100644 index 00000000..a1b24f4c --- /dev/null +++ b/src/generic/layer2_json/CMakeLists.txt @@ -0,0 +1,17 @@ +# set library parameters +INCLUDE(FindPkgConfig) + +pkg_check_modules(LIBJANSSON jansson) +if (LIBJANSSON_FOUND) + include_directories(${LIBJANSSON_INCLUDE_DIRS}) + link_directories(${LIBJANSSON_LIBRARY_DIRS}) + + # set library parameters + SET (srcs layer2json.c l2json_import.c l2json_export.c) + SET (hdrs layer2_json.h layer2_json_internal.h) + + message(STATUS "Using '${LIBJANSSON_LIBRARIES}' for json handling") + + # use generic plugin maker + oonf_create_plugin("layer2_json" "${srcs}" "${hdrs}" "${LIBJANSSON_LIBRARIES}") +endif (LIBJANSSON_FOUND) diff --git a/src/generic/layer2_json/l2json_export.c b/src/generic/layer2_json/l2json_export.c new file mode 100644 index 00000000..db3a7667 --- /dev/null +++ b/src/generic/layer2_json/l2json_export.c @@ -0,0 +1,351 @@ + +/* + * The olsr.org Optimized Link-State Routing daemon version 2 (olsrd2) + * Copyright (c) 2004-2015, the olsr.org team - see HISTORY file + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of olsr.org, olsrd nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * Visit http://www.olsr.org for more information. + * + * If you find this software useful feel free to make a donation + * to the project. For more information see the website or contact + * the copyright holders. + * + */ + +/** + * @file + */ + +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +static int _export_root(json_error_t *error, json_t *root); +static int _cb_dump(const char *buffer, size_t size, void *data); + +static enum oonf_log_source LOG_LAYER2_JSON; + +void +l2json_export_init(enum oonf_log_source log) { + LOG_LAYER2_JSON = log; +} + +void +l2json_export_cleanup(void) { +} + +int l2json_export(struct oonf_telnet_data *con) { + json_t *root; + json_error_t error; + int rc; + + rc = -1; + root = json_object(); + if (!root) { + goto export_fail; + } + + if ((rc = _export_root(&error, root))) { + OONF_DEBUG(LOG_LAYER2_JSON, "Error: %d (%s/%s)", + rc, error.text, error.source); + goto export_fail; + } + + if (json_dump_callback(root, _cb_dump, con, JSON_ENSURE_ASCII)) { + goto export_fail; + } + + abuf_puts(con->out, "\n"); + + rc = 0; + +export_fail: + json_decref(root); + return rc; +} + +static int +_add_data_object(json_error_t *error, + json_t *array, struct oonf_layer2_data *l2data, + const struct oonf_layer2_metadata *l2meta) { + const struct oonf_layer2_origin *l2origin; + char l2vbuf[256]; + + if (!oonf_layer2_data_has_value(l2data)) { + /* skip this data object */ + return 0; + } + + l2origin = oonf_layer2_data_get_origin(l2data); + if (!oonf_layer2_data_to_string(l2vbuf, sizeof(l2vbuf), l2data, l2meta, false)) { + return __LINE__; + } + + switch (oonf_layer2_data_get_type(l2data)) { + case OONF_LAYER2_INTEGER_DATA: + if (json_array_append_new(array, + json_pack_ex(error, sizeof(*error), + "{ss ss ss sI ss ss sI}", + "type", oonf_layer2_data_get_type_string(l2meta), + "key", l2meta->key, + "value", l2vbuf, + "intvalue", oonf_layer2_data_get_int64(l2data, 0, 0), + "origin", l2origin->name, + "unit", l2meta->unit, + "scaling", l2meta->scaling + ))) { + return __LINE__; + } + break; + case OONF_LAYER2_BOOLEAN_DATA: + if (json_array_append_new(array, + json_pack_ex(error, sizeof(*error), + "{ss ss ss sb ss}", + "type", oonf_layer2_data_get_type_string(l2meta), + "key", l2meta->key, + "value", l2vbuf, + "boolvalue", oonf_layer2_data_get_boolean(l2data, false), + "origin", l2origin->name + ))) { + return __LINE__; + } + break; + default: + if (json_array_append_new(array, + json_pack_ex(error, sizeof(*error), + "{ss ss ss ss}", + "type", oonf_layer2_data_get_type_string(l2meta), + "key", l2meta->key, + "value", l2vbuf, + "origin", l2origin->name + ))) { + return __LINE__; + } + break; + } + return 0; +} + +static int +_export_l2neigh(json_error_t *error, + json_t *j_neighs, struct oonf_layer2_neigh *l2neigh) { + struct oonf_layer2_neighbor_address *l2remoteip; + struct oonf_layer2_destination *l2proxy; + struct oonf_layer2_data *l2data; + const struct oonf_layer2_metadata *l2meta; + enum oonf_layer2_neighbor_index neighidx; + int result; + const char *ll4, *ll6; + struct netaddr_str nbuf1, nbuf2, nbuf3; + char hexbuf[OONF_LAYER2_MAX_LINK_ID*3]; + int64_t lastseen; + + json_t *j_proxy; + json_t *j_remoteip; + json_t *j_neighdata; + + j_proxy = json_array(); + j_remoteip = json_array(); + j_neighdata = json_array(); + + if (-1 == strhex_from_bin(hexbuf, sizeof(hexbuf), + l2neigh->key.link_id, + l2neigh->key.link_id_length)) { + return __LINE__; + } + ll4 = NULL; + if (oonf_layer2_neigh_has_nexthop(l2neigh, AF_INET)) { + ll4 = netaddr_to_string(&nbuf2, oonf_layer2_neigh_get_nexthop(l2neigh, AF_INET)); + } + ll6 = NULL; + if (oonf_layer2_neigh_has_nexthop(l2neigh, AF_INET6)) { + ll6 = netaddr_to_string(&nbuf3, oonf_layer2_neigh_get_nexthop(l2neigh, AF_INET6)); + } + lastseen = -oonf_clock_get_relative( + oonf_layer2_neigh_get_lastseen(l2neigh) + ); + if (json_array_append_new(j_neighs, + json_pack_ex(error, sizeof(*error), + "{s{ss ss} ss* ss* sI so so so}", + "key", + "addr", netaddr_to_string(&nbuf1, &l2neigh->key.addr), + "link_id", hexbuf, + "ll_ipv4", ll4, + "ll_ipv6", ll6, + "last_seen", lastseen, + "proxy_addr", j_proxy, + "remote_ip", j_remoteip, + "data", j_neighdata + ))) { + return __LINE__; + } + + avl_for_each_element(&l2neigh->destinations, l2proxy, _node) { + if (json_array_append_new(j_proxy, + json_pack_ex(error, sizeof(*error), + "{ss ss}", + "addr", netaddr_to_string(&nbuf1, &l2proxy->destination), + "origin", l2proxy->origin->name + ))) { + return __LINE__; + } + } + + avl_for_each_element(&l2neigh->remote_neighbor_ips, l2remoteip, _neigh_node) { + if (json_array_append_new(j_remoteip, + json_pack("{ss ss}", + "addr", netaddr_to_string(&nbuf1, &l2remoteip->ip), + "origin", l2remoteip->origin->name + ))) { + return __LINE__; + } + } + + for (neighidx = 0; neighidx < OONF_LAYER2_NEIGH_COUNT; neighidx++) { + l2data = &l2neigh->data[neighidx]; + l2meta = oonf_layer2_neigh_metadata_get(neighidx); + if ((result =_add_data_object(error, j_neighdata, l2data, l2meta))) { + return result; + } + } + return 0; +} + +static int +_export_l2net(json_error_t *error, + json_t *j_nets, struct oonf_layer2_net *l2net) { + struct oonf_layer2_peer_address *l2peer; + struct oonf_layer2_neigh *l2neigh; + struct oonf_layer2_data *l2data; + const struct oonf_layer2_metadata *l2meta; + enum oonf_layer2_network_index netidx; + enum oonf_layer2_neighbor_index neighidx; + int result; + + json_t *j_ips, *j_neighs; + json_t *j_netdata, *j_neighdata; + + struct netaddr_str nbuf; + j_ips = json_array(); + j_netdata = json_array(); + j_neighdata = json_array(); + j_neighs = json_array(); + + if (json_array_append_new(j_nets, + json_pack_ex(error, sizeof(*error), + "{ss ss ss sb sI so so so so}", + "name", l2net->name, + "ident", l2net->if_ident, + "type", oonf_layer2_net_get_type_name(l2net->if_type), + "dlep", l2net->if_dlep, + "last_seen", -oonf_clock_get_relative(l2net->last_seen), + "local_peers", j_ips, + "data", j_netdata, + "neighbor_defaults", j_neighdata, + "neighbors", j_neighs + ))) { + return __LINE__; + } + + avl_for_each_element(&l2net->local_peer_ips, l2peer, _net_node) { + if(json_array_append_new( + j_ips, json_pack_ex(error, sizeof(*error), + "{ss ss}", + "ip", netaddr_to_string(&nbuf, &l2peer->ip), + "origin", l2peer->origin->name + ))) { + return __LINE__; + } + } + + for (netidx = 0; netidx < OONF_LAYER2_NET_COUNT; netidx++) { + l2data = &l2net->data[netidx]; + l2meta = oonf_layer2_net_metadata_get(netidx); + if ((result =_add_data_object(error, j_netdata, l2data, l2meta))) { + return result; + } + } + + for (neighidx = 0; neighidx < OONF_LAYER2_NEIGH_COUNT; neighidx++) { + l2data = &l2net->neighdata[neighidx]; + l2meta = oonf_layer2_neigh_metadata_get(neighidx); + if ((result =_add_data_object(error, j_neighdata, l2data, l2meta))) { + return result; + } + } + + avl_for_each_element(&l2net->neighbors, l2neigh, _node) { + if ((result = _export_l2neigh(error, j_neighs, l2neigh))) { + return result; + } + } + return 0; +} + +static int +_export_root(json_error_t *error, json_t *root) { + struct oonf_layer2_net *l2net; + json_t *j_nets; + int result; + + if (!(j_nets = json_array())) + return __LINE__; + + if (json_object_set_new(root, "interfaces", j_nets)) + return __LINE__; + + avl_for_each_element(oonf_layer2_get_net_tree(), l2net, _node) { + if ((result = _export_l2net(error, j_nets, l2net))) { + return result; + } + } + return 0; +} + +static int +_cb_dump(const char *buffer, size_t size, void *data) { + struct oonf_telnet_data *con = data; + return abuf_memcpy(con->out, buffer, size); +} + diff --git a/src/generic/layer2_json/l2json_import.c b/src/generic/layer2_json/l2json_import.c new file mode 100644 index 00000000..a5896858 --- /dev/null +++ b/src/generic/layer2_json/l2json_import.c @@ -0,0 +1,547 @@ + +/* + * The olsr.org Optimized Link-State Routing daemon version 2 (olsrd2) + * Copyright (c) 2004-2015, the olsr.org team - see HISTORY file + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of olsr.org, olsrd nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * Visit http://www.olsr.org for more information. + * + * If you find this software useful feel free to make a donation + * to the project. For more information see the website or contact + * the copyright holders. + * + */ + +/** + * @file + */ + +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include + +struct l2json_origin { + struct oonf_layer2_origin origin; + char name[32]; + struct list_entity _node; +}; + +static int _import_l2net(json_error_t *error, json_t *j_net); +static bool _is_array_or_null(json_t *j); + +static struct oonf_class _origin_class = { + .name = "l2json origin", + .size = sizeof(struct l2json_origin) +}; + +static struct list_entity _origin_list; +static enum oonf_log_source LOG_LAYER2_JSON; + +void +l2json_import_init(enum oonf_log_source log) { + LOG_LAYER2_JSON = log; + + oonf_class_add(&_origin_class); + list_init_head(&_origin_list); +} + +void l2json_import_cleanup(void) { + struct l2json_origin *ptr, *safe; + + list_for_each_element_safe(&_origin_list, ptr, _node, safe) { + list_remove(&ptr->_node); + oonf_class_free(&_origin_class, ptr); + } + + oonf_class_remove(&_origin_class); +} + +int +l2json_import(struct oonf_telnet_data __attribute__((unused)) *con, + const char *input) { + json_t *root, *j_nets, *j_net; + json_error_t error; + size_t idx; + int rc; + + rc = -1; + + OONF_DEBUG(LOG_LAYER2_JSON, "Import: '%s'", input); + root = json_loads(input, JSON_REJECT_DUPLICATES, &error); + if (!root) { + goto import_fail; + } + + if (json_unpack_ex(root, &error, sizeof(error), + "{so}", + "interfaces", &j_nets)) { + OONF_DEBUG(LOG_LAYER2_JSON, "Error:(%s/%s)", + error.text, error.source); + goto import_fail; + } + + if (!_is_array_or_null(j_nets)) { + goto import_fail; + } + + json_array_foreach(j_nets, idx, j_net) { + if ((rc = _import_l2net(&error, j_net))) { + OONF_DEBUG(LOG_LAYER2_JSON, "Error: %d (%s/%s)", + rc, error.text, error.source); + goto import_fail; + } + } + rc = 0; + +import_fail: + json_decref(root); + return rc; +} + +static struct oonf_layer2_origin * +_get_origin(const char *name) { + struct oonf_layer2_origin *origin; + struct l2json_origin *jsonorigin; + + origin = oonf_layer2_origin_get(name); + if (origin) { + return origin; + } + jsonorigin = oonf_class_malloc(&_origin_class); + if (!jsonorigin) { + return NULL; + } + list_add_tail(&_origin_list, &jsonorigin->_node); + strscpy(jsonorigin->name, name, sizeof(jsonorigin->name)); + jsonorigin->origin.name = jsonorigin->name; + + oonf_layer2_origin_add(&jsonorigin->origin); + return &jsonorigin->origin; +} + +static bool +_is_array_or_null(json_t *j) { + return j == NULL || json_is_array(j); +} + +static int +_import_local_peer(json_error_t *error, + struct oonf_layer2_net *l2net, json_t *j_ip) { + struct oonf_layer2_origin *l2origin; + struct netaddr ipbuf; + char *ip, *origin; + + if(json_unpack_ex(j_ip, error, sizeof(*error), + "{ss ss}", + "ip", &ip, + "origin", &origin)) { + return __LINE__; + } + + if (netaddr_from_string(&ipbuf, ip)) { + return __LINE__; + } + + l2origin = _get_origin(origin); + if (!l2origin) { + return __LINE__; + } + + if(!oonf_layer2_net_add_ip(l2net, l2origin, &ipbuf)) { + return __LINE__; + } + return 0; +} + +static int +_import_destination(json_error_t *error, + struct oonf_layer2_neigh *l2neigh, json_t *j_dst) { + struct oonf_layer2_origin *l2origin; + struct netaddr ipbuf; + char *ip, *origin; + + if(json_unpack_ex(j_dst, error, sizeof(*error), + "{ss ss}", + "addr", &ip, + "origin", &origin)) { + return __LINE__; + } + + if (netaddr_from_string(&ipbuf, ip)) { + return __LINE__; + } + + l2origin = _get_origin(origin); + if (!l2origin) { + return __LINE__; + } + + if(!oonf_layer2_destination_add(l2neigh, &ipbuf, l2origin)) { + return __LINE__; + } + return 0; +} + +static int +_import_remoteip(json_error_t *error, + struct oonf_layer2_neigh *l2neigh, json_t *j_rip) { + struct oonf_layer2_origin *l2origin; + struct netaddr ipbuf; + char *ip, *origin; + + if(json_unpack_ex(j_rip, error, sizeof(*error), + "{ss ss}", + "addr", &ip, + "origin", &origin)) { + return __LINE__; + } + + if (netaddr_from_string(&ipbuf, ip)) { + return __LINE__; + } + + l2origin = _get_origin(origin); + if (!l2origin) { + return __LINE__; + } + + if(!oonf_layer2_neigh_add_ip(l2neigh, l2origin, &ipbuf)) { + return __LINE__; + } + return 0; +} + +static int +_set_data(struct oonf_layer2_data *data, + const struct oonf_layer2_metadata *meta, const char *originname, + const char *value, int64_t intvalue, bool boolvalue) { + struct oonf_layer2_origin *origin; + union oonf_layer2_value l2value; + + origin = _get_origin(originname); + if (!origin) { + return __LINE__; + } + + if (value) { + if (oonf_layer2_data_parse_string(&l2value, meta, value)) { + OONF_WARN(LOG_LAYER2_JSON, "%s: %s", meta->key, value); + return __LINE__; + } + } + else if (meta->type == OONF_LAYER2_INTEGER_DATA) { + l2value.integer = intvalue; + } + else if (meta->type == OONF_LAYER2_BOOLEAN_DATA) { + l2value.boolean = boolvalue; + } + else { + return __LINE__; + } + + oonf_layer2_data_set(data, origin, meta, &l2value); + return 0; +} + +static int +_import_net_data(json_error_t *error, struct oonf_layer2_data *data, + json_t *j_netdata) { + const struct oonf_layer2_metadata *meta; + enum oonf_layer2_network_index idx; + char *key, *origin, *value; + int64_t intvalue; + bool boolvalue; + + key = NULL; + origin = NULL; + value = NULL; + intvalue = 0; + boolvalue = false; + + if (json_unpack_ex(j_netdata, error, sizeof(*error), + "{ss ss s?s s?I s?b}", + "key", &key, + "origin", &origin, + "value", &value, + "intvalue", &intvalue, + "boolvalue", &boolvalue + )) { + return __LINE__; + } + + for (idx = 0; idxkey, key) == 0) { + return _set_data(&data[idx], meta, origin, value, intvalue, boolvalue); + } + } + return __LINE__; +} + +static int +_import_neigh_data(json_error_t *error, struct oonf_layer2_data *data, + json_t *j_netdata) { + const struct oonf_layer2_metadata *meta; + enum oonf_layer2_neighbor_index idx; + char *key, *origin, *value; + int64_t intvalue; + bool boolvalue; + + key = NULL; + origin = NULL; + value = NULL; + intvalue = 0; + boolvalue = false; + + if (json_unpack_ex(j_netdata, error, sizeof(*error), + "{ss ss s?s s?I s?b}", + "key", &key, + "origin", &origin, + "value", &value, + "intvalue", &intvalue, + "boolvalue", &boolvalue + )) { + return __LINE__; + } + + for (idx = 0; idxkey, key) == 0) { + return _set_data(&data[idx], meta, origin, value, intvalue, boolvalue); + } + } + return __LINE__; +} + +static int +_import_l2neigh(json_error_t *error, json_t *j_neigh, + struct oonf_layer2_net *l2net) { + char *addr, *link_id_str; + char *next4, *next6; + json_t *j_dest, *j_remoteip; + int64_t last_seen; + json_t *j_neighdata; + + json_t *j_item; + size_t idx; + int rc; + + struct oonf_layer2_neigh *l2neigh; + struct oonf_layer2_neigh_key key; + ssize_t len; + + memset(&key, 0, sizeof(key)); + + link_id_str = NULL; + next4 = NULL; + next6 = NULL; + + if(json_unpack_ex(j_neigh, error, sizeof(*error), + "{s{ss s?s} s?s s?s s?I s?o s?o s?o}", + "key", + "addr", &addr, + "link_id", &link_id_str, + "ll_ipv4", &next4, + "ll_ipv6", &next6, + "last_seen", &last_seen, + "proxy_addr", &j_dest, + "remote_ip", &j_remoteip, + "data", &j_neighdata)) { + return __LINE__; + } + + if (netaddr_from_string(&key.addr, addr)) { + OONF_WARN(LOG_LAYER2_JSON, "Bad neighbor key: %s", addr); + return __LINE__; + } + if (link_id_str) { + len = strhex_to_bin(key.link_id, sizeof(key.link_id), link_id_str); + if (len < 0 || len > OONF_LAYER2_MAX_LINK_ID) { + OONF_WARN(LOG_LAYER2_JSON, "Bad link_id for neighbor %s: %s", addr, link_id_str); + return __LINE__; + } + key.link_id_length = len; + } + + if (!(l2neigh = oonf_layer2_neigh_add_lid(l2net, &key))) { + return __LINE__; + } + + if (next4) { + if (netaddr_from_string(&l2neigh->_next_hop_v4, next4)) { + OONF_WARN(LOG_LAYER2_JSON, "Bad ll_ipv4 for neighbor (%s/%s): %s", + addr, link_id_str, next4); + return __LINE__; + } + } + if (next6) { + if (netaddr_from_string(&l2neigh->_next_hop_v6, next6)) { + OONF_WARN(LOG_LAYER2_JSON, "Bad ll_ipv6 for neighbor (%s/%s): %s", + addr, link_id_str, next6); + return __LINE__; + } + } + + if (last_seen) { + l2net->last_seen = oonf_clock_get_absolute(-last_seen); + } + + if (j_dest) { + json_array_foreach(j_dest, idx, j_item) { + if ((rc = _import_destination(error, l2neigh, j_item))) { + return rc; + } + } + } + + if (j_remoteip) { + json_array_foreach(j_remoteip, idx, j_item) { + if ((rc = _import_remoteip(error, l2neigh, j_item))) { + return rc; + } + } + } + + if (j_neighdata) { + json_array_foreach(j_neighdata, idx, j_item) { + if ((rc = _import_neigh_data(error, &l2neigh->data[0], j_item))) { + return rc; + } + } + } + return 0; +} + +static int +_import_l2net(json_error_t *error, json_t *j_net) { + const char *name, *if_ident, *if_type; + int if_dlep; + int64_t last_seen; + json_t *j_ips, *j_netdata, *j_neighdata, *j_neighs, *j_item; + size_t idx; + int rc; + struct oonf_layer2_net *l2net; + + name = NULL; + if_ident = NULL; + if_type = NULL; + if_dlep = -1; + last_seen = 0; + j_ips = NULL; + j_netdata = NULL; + j_neighdata = NULL; + j_neighs = NULL; + if(json_unpack_ex(j_net, error, sizeof(*error), + "{ss s?s s?s s?b s?I s?o s?o s?o s?o}", + "name", &name, + "ident", &if_ident, + "type", &if_type, + "dlep", &if_dlep, + "last_seen", &last_seen, + "local_peers", &j_ips, + "data", &j_netdata, + "neighbor_defaults", &j_neighdata, + "neighbors", &j_neighs)) { + return __LINE__; + } + if (!_is_array_or_null(j_ips)) { + return __LINE__; + } + if (!_is_array_or_null(j_netdata)) { + return __LINE__; + } + if (!_is_array_or_null(j_neighdata)) { + return __LINE__; + } + if (!_is_array_or_null(j_neighs)) { + return __LINE__; + } + + l2net = oonf_layer2_net_add(name); + if (!l2net) { + return __LINE__; + } + if (if_ident) { + strscpy(l2net->if_ident, if_ident, sizeof(l2net->if_ident)); + } + if (if_type) { + l2net->if_type = oonf_layer2_get_type(if_type); + } + if (if_dlep != -1) { + l2net->if_dlep = if_dlep != 0; + } + if (last_seen) { + l2net->last_seen = oonf_clock_get_absolute(-last_seen); + } + if (j_ips) { + json_array_foreach(j_ips, idx, j_item) { + if ((rc = _import_local_peer(error, l2net, j_item))) { + return rc; + } + } + } + if (j_netdata) { + json_array_foreach(j_netdata, idx, j_item) { + if ((rc = _import_net_data(error, &l2net->data[0], j_item))) { + return rc; + } + } + } + if (j_neighdata) { + json_array_foreach(j_neighdata, idx, j_item) { + if ((rc = _import_neigh_data(error, &l2net->neighdata[0], j_item))) { + return rc; + } + } + } + if (j_neighs) { + json_array_foreach(j_neighs, idx, j_item) { + if ((rc = _import_l2neigh(error, j_item, l2net))) { + return rc; + } + } + } + return 0; +} diff --git a/src/generic/layer2_json/layer2json.c b/src/generic/layer2_json/layer2json.c new file mode 100644 index 00000000..23e53d9e --- /dev/null +++ b/src/generic/layer2_json/layer2json.c @@ -0,0 +1,140 @@ + +/* + * The olsr.org Optimized Link-State Routing daemon version 2 (olsrd2) + * Copyright (c) 2004-2015, the olsr.org team - see HISTORY file + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of olsr.org, olsrd nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * Visit http://www.olsr.org for more information. + * + * If you find this software useful feel free to make a donation + * to the project. For more information see the website or contact + * the copyright holders. + * + */ + +/** + * @file + */ + +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include + +/* definitions */ +#define LOG_LAYER2_JSON _oonf_layer2json_subsystem.logging + +/* prototypes */ +static int _init(void); +static void _cleanup(void); + +static enum oonf_telnet_result _cb_layer2json(struct oonf_telnet_data *con); + +/* telnet command of this plugin */ +static struct oonf_telnet_command _telnet_commands[] = { + TELNET_CMD(OONF_LAYER2JSON_SUBSYSTEM, _cb_layer2json, "TODO: Help!"), +}; + +/* plugin declaration */ +static const char *_dependencies[] = { + OONF_CLOCK_SUBSYSTEM, + OONF_LAYER2_SUBSYSTEM, + OONF_TELNET_SUBSYSTEM, +}; + +static struct oonf_subsystem _oonf_layer2json_subsystem = { + .name = OONF_LAYER2JSON_SUBSYSTEM, + .dependencies = _dependencies, + .dependencies_count = ARRAYSIZE(_dependencies), + .descr = "OLSRv2 layer2 json plugin", + .author = "Henning Rogge", + .init = _init, + .cleanup = _cleanup, +}; +DECLARE_OONF_PLUGIN(_oonf_layer2json_subsystem); + +/** + * Initialize plugin + * @return -1 if an error happened, 0 otherwise + */ +static int +_init(void) { + oonf_telnet_add(&_telnet_commands[0]); + l2json_import_init(LOG_LAYER2_JSON); + l2json_export_init(LOG_LAYER2_JSON); + return 0; +} + +/** + * Cleanup plugin + */ +static void +_cleanup(void) { + l2json_export_cleanup(); + l2json_import_cleanup(); + oonf_telnet_remove(&_telnet_commands[0]); +} + +/** + * Callback for the telnet command of this plugin + * @param con pointer to telnet session data + * @return telnet result value + */ +static enum oonf_telnet_result +_cb_layer2json(struct oonf_telnet_data *con) { + const char *second; + + if ((second = str_hasnextword(con->parameter, "import"))) { + if (l2json_import(con, second)) { + return TELNET_RESULT_INTERNAL_ERROR; + } + } + if ((second = str_hasnextword(con->parameter, "export"))) { + if (l2json_export(con)) { + return TELNET_RESULT_INTERNAL_ERROR; + } + } + + return 0; +} diff --git a/src/generic/link_config/link_config.c b/src/generic/link_config/link_config.c index 6f7d784d..66bfeffa 100644 --- a/src/generic/link_config/link_config.c +++ b/src/generic/link_config/link_config.c @@ -293,7 +293,7 @@ _parse_strarray(struct strarray *array, const char *ifname, enum oonf_layer2_nei strarray_for_each_element(array, entry) { ptr = str_cpynextword(hbuf.buf, entry, sizeof(hbuf)); - if (isonumber_to_s64(&value, hbuf.buf, meta->scaling)) { + if (isonumber_to_s64(&value, hbuf.buf, meta->unit, meta->scaling)) { continue; } diff --git a/src/libcommon/isonumber.c b/src/libcommon/isonumber.c index 59585d54..d2b87ba1 100644 --- a/src/libcommon/isonumber.c +++ b/src/libcommon/isonumber.c @@ -50,6 +50,7 @@ #include #include +static int64_t _get_scale(const char *text, const char *unit); static const char *_isonumber_u64_to_string( char *out, size_t out_len, uint64_t number, const char *unit, uint64_t scaling, bool raw); @@ -118,11 +119,12 @@ isonumber_from_s64(struct isonumber_str *out, int64_t number, const char *unit, * to a signed 64bit integer. * @param dst pointer to destination variable * @param iso pointer to string source + * @param unit (optional) iso unit allowed at the end of the number * @param scaling fixed point integer arithmetics scaling factor * @return -1 if an error happened, 0 otherwise */ int -isonumber_to_s64(int64_t *dst, const char *iso, uint64_t scaling) { +isonumber_to_s64(int64_t *dst, const char *iso, const char *unit, uint64_t scaling) { const char *ptr; int result; uint64_t u64; @@ -132,7 +134,7 @@ isonumber_to_s64(int64_t *dst, const char *iso, uint64_t scaling) { ptr++; } - result = isonumber_to_u64(&u64, ptr, scaling); + result = isonumber_to_u64(&u64, ptr, unit, scaling); if (!result) { if (*iso == '-') { *dst = -((int64_t)u64); @@ -149,20 +151,19 @@ isonumber_to_s64(int64_t *dst, const char *iso, uint64_t scaling) { * to an unsigned 64bit integer. * @param dst pointer to destination variable * @param iso pointer to string source + * @param unit (optional) iso unit allowed at the end of the number * @param scaling fixed point integer arithmetics scaling factor * @return -1 if an error happened, 0 otherwise */ int -isonumber_to_u64(uint64_t *dst, const char *iso, uint64_t scaling) { - static const char symbol_large[] = " kMGTPE"; - static const char symbol_small[] = " munpfa"; - +isonumber_to_u64(uint64_t *dst, const char *iso, const char *unit, uint64_t scaling) { uint64_t num, fraction_scale, factor; - char *next = NULL, *prefix; + int64_t scale; + char *next = NULL; errno = 0; num = strtoull(iso, &next, 10); - if (errno) { + if (errno || next == iso) { return -1; } @@ -197,50 +198,96 @@ isonumber_to_u64(uint64_t *dst, const char *iso, uint64_t scaling) { next++; } - factor = 1; - if (*next) { - /* handle iso-prefix */ - if (next[1] != 0) { - return -1; + scale = _get_scale(next, unit); + if (scale > 0) { + factor = scale; + } + else if (scale < 0) { + factor = 1; + fraction_scale = fraction_scale * (-scale); + } + else { + return -1; + } + + while (fraction_scale > scaling && fraction_scale >= 1000) { + fraction_scale /= 1000; + if (factor > 1) { + factor /= 1000; } + else { + num /= 1000; + } + } - prefix = strchr(symbol_large, next[0]); + if (num > UINT64_MAX / factor) { + /* this would be an integer overflow */ + return -1; + } + + *dst = num * factor * scaling / fraction_scale; + return 0; +} + +/** + * Derive the scaling depending on the iso prefix. + * Ignore whitespaces and a trailing unit if present. + * @param text the text to parse + * @param unit unit string or NULL + * @return positive scaling factor, negative fractional scaling, + * 0 for error. + */ +static int64_t +_get_scale(const char *text, const char *unit) { + static const char symbol_large[] = " kMGTPE"; + static const char symbol_small[] = " munpfa"; + + const char *prefix; + int64_t factor; + + factor = 1; + + /* skip whitespaces */ + while (*text == ' ') { + text++; + } + + /* Look for prefixes for large numbers */ + if (*text) { + prefix = strchr(symbol_large, text[0]); if (prefix) { + factor = 1; while (prefix > symbol_large) { factor *= 1000; prefix--; } + text++; } else { - prefix = strchr(symbol_small, next[0]); + /* look for prefixes for small numbers */ + prefix = strchr(symbol_small, text[0]); if (prefix) { + factor = -1; while (prefix > symbol_small) { - fraction_scale *= 1000; + factor *= 1000; prefix--; } - } - else { - return -1; + text++; } } } - while (fraction_scale > scaling && fraction_scale >= 1000) { - fraction_scale /= 1000; - if (factor > 1) { - factor /= 1000; - } - else { - num /= 1000; - } + /* skip whitespaces again, just to be sure */ + while (*text == ' ') { + text++; } - if (num > UINT64_MAX / factor) { - /* this would be an integer overflow */ - return -1; + /* the end is either the unit or just nothing */ + if (*text == 0 || (unit != NULL && strcasecmp(text, unit) == 0)) { + return factor; } - *dst = num * factor * scaling / fraction_scale; + /* bad result */ return 0; } diff --git a/src/libconfig/cfg_tobin.c b/src/libconfig/cfg_tobin.c index e88c026e..1c43cd34 100644 --- a/src/libconfig/cfg_tobin.c +++ b/src/libconfig/cfg_tobin.c @@ -116,7 +116,7 @@ cfg_tobin_int(void *reference, size_t bin_size, const struct const_strarray *val for (j = 0, scaling = 1; j < fraction; j++, scaling*=10); - result = isonumber_to_s64(&i, strarray_get_first_c(value), scaling); + result = isonumber_to_s64(&i, strarray_get_first_c(value), NULL, scaling); if (result == 0) { switch (int_size) { case 4: diff --git a/src/libconfig/cfg_validate.c b/src/libconfig/cfg_validate.c index e478f164..b0d446c4 100644 --- a/src/libconfig/cfg_validate.c +++ b/src/libconfig/cfg_validate.c @@ -151,7 +151,7 @@ cfg_validate_int(struct autobuf *out, const char *section_name, const char *entr struct isonumber_str hbuf; for (j = 0, scaling = 1; j < fraction; j++, scaling*=10); - if (isonumber_to_s64(&i, value, scaling)) { + if (isonumber_to_s64(&i, value, NULL, scaling)) { if (fraction) { cfg_append_printable_line(out, "Value '%s' for entry '%s'" diff --git a/src/tests/common/test_common_isonumber.c b/src/tests/common/test_common_isonumber.c index fb1424b9..c99f5975 100644 --- a/src/tests/common/test_common_isonumber.c +++ b/src/tests/common/test_common_isonumber.c @@ -107,7 +107,7 @@ test_isonumber_to_u64_from_string(void) { for (scaling = 1; scaling <= 64; scaling *= 4) { for (i=0; i Date: Fri, 11 Jun 2021 13:16:20 +0200 Subject: [PATCH 54/63] ANSN Check must be skipped if we reset the sequence number of a router --- include/oonf/base/oonf_duplicate_set.h | 5 ++++- include/oonf/olsrv2/olsrv2/olsrv2.h | 2 +- src/base/oonf_duplicate_set.c | 2 +- src/olsrv2/olsrv2/olsrv2.c | 6 +++++- src/olsrv2/olsrv2/olsrv2_reader.c | 8 +++++--- 5 files changed, 16 insertions(+), 7 deletions(-) diff --git a/include/oonf/base/oonf_duplicate_set.h b/include/oonf/base/oonf_duplicate_set.h index 06f08d87..b99ff7d9 100644 --- a/include/oonf/base/oonf_duplicate_set.h +++ b/include/oonf/base/oonf_duplicate_set.h @@ -86,6 +86,9 @@ enum oonf_duplicate_result /*! newer than the latest cached number */ OONF_DUPSET_NEWEST, + /*! sequence number was reset */ + OONF_DUPSET_RESET, + /*! sequence number was the first tested with the duplicate set */ OONF_DUPSET_FIRST, }; @@ -180,7 +183,7 @@ EXPORT const char *oonf_duplicate_get_result_str(enum oonf_duplicate_result); */ static INLINE bool oonf_duplicate_is_new(enum oonf_duplicate_result result) { - return result == OONF_DUPSET_NEW || result == OONF_DUPSET_NEWEST || result == OONF_DUPSET_FIRST; + return result == OONF_DUPSET_NEW || result == OONF_DUPSET_NEWEST || result == OONF_DUPSET_RESET || result == OONF_DUPSET_FIRST; } #endif /* OONF_DUPLICATE_SET_H_ */ diff --git a/include/oonf/olsrv2/olsrv2/olsrv2.h b/include/oonf/olsrv2/olsrv2/olsrv2.h index 792a6d21..153fa3fb 100644 --- a/include/oonf/olsrv2/olsrv2/olsrv2.h +++ b/include/oonf/olsrv2/olsrv2/olsrv2.h @@ -75,7 +75,7 @@ EXPORT uint64_t olsrv2_get_tc_interval(void); EXPORT uint64_t olsrv2_get_tc_validity(void); EXPORT bool olsrv2_is_nhdp_routable(struct netaddr *addr); EXPORT bool olsrv2_is_routable(struct netaddr *addr); -EXPORT bool olsrv2_mpr_shall_process(struct rfc5444_reader_tlvblock_context *, uint64_t vtime); +EXPORT bool olsrv2_mpr_shall_process(struct rfc5444_reader_tlvblock_context *, uint64_t vtime, bool *); EXPORT bool olsrv2_mpr_shall_forwarding( struct rfc5444_reader_tlvblock_context *context, struct netaddr *source_address, uint64_t vtime); EXPORT void olsrv2_generate_tcs(bool); diff --git a/src/base/oonf_duplicate_set.c b/src/base/oonf_duplicate_set.c index 48c061d8..c1f978d2 100644 --- a/src/base/oonf_duplicate_set.c +++ b/src/base/oonf_duplicate_set.c @@ -319,7 +319,7 @@ _test(struct oonf_duplicate_set *dupset, struct oonf_duplicate_entry *entry, uin entry->too_old_count = 0; entry->current = seqno; - return OONF_DUPSET_NEWEST; + return OONF_DUPSET_RESET; } return OONF_DUPSET_TOO_OLD; } diff --git a/src/olsrv2/olsrv2/olsrv2.c b/src/olsrv2/olsrv2/olsrv2.c index 8530cc98..e360293d 100644 --- a/src/olsrv2/olsrv2/olsrv2.c +++ b/src/olsrv2/olsrv2/olsrv2.c @@ -384,10 +384,11 @@ olsrv2_is_routable(struct netaddr *addr) { * to MPR settings. * @param context RFC5444 tlvblock reader context * @param vtime validity time for duplicate entry data + * @param reset pointer to boolean that will tell if the sequence number was reset * @return true if TC should be processed, false otherwise */ bool -olsrv2_mpr_shall_process(struct rfc5444_reader_tlvblock_context *context, uint64_t vtime) { +olsrv2_mpr_shall_process(struct rfc5444_reader_tlvblock_context *context, uint64_t vtime, bool *reset) { enum oonf_duplicate_result dup_result; bool process; #ifdef OONF_LOG_DEBUG_INFO @@ -407,6 +408,9 @@ olsrv2_mpr_shall_process(struct rfc5444_reader_tlvblock_context *context, uint64 dup_result = oonf_duplicate_entry_add(&_protocol->processed_set, context->msg_type, &context->orig_addr, context->seqno, vtime + _olsrv2_config.f_hold_time); process = oonf_duplicate_is_new(dup_result); + if (reset != NULL) { + *reset = dup_result == OONF_DUPSET_RESET; + } OONF_DEBUG(LOG_OLSRV2, "Do %sprocess message type %u from %s" diff --git a/src/olsrv2/olsrv2/olsrv2_reader.c b/src/olsrv2/olsrv2/olsrv2_reader.c index 1d8c80f5..8748ae26 100644 --- a/src/olsrv2/olsrv2/olsrv2_reader.c +++ b/src/olsrv2/olsrv2/olsrv2_reader.c @@ -215,6 +215,7 @@ _cb_messagetlvs(struct rfc5444_reader_tlvblock_context *context) { uint16_t ansn; uint8_t tmp; int af_type; + bool seqno_reset; #ifdef OONF_LOG_DEBUG_INFO struct netaddr_str buf; #endif @@ -295,7 +296,8 @@ _cb_messagetlvs(struct rfc5444_reader_tlvblock_context *context) { } /* test if we already processed the message */ - if (!olsrv2_mpr_shall_process(context, _current.vtime)) { + seqno_reset = false; + if (!olsrv2_mpr_shall_process(context, _current.vtime, &seqno_reset)) { OONF_DEBUG(LOG_OLSRV2_R, "Processing set says 'do not process'"); return RFC5444_DROP_MSG_BUT_FORWARD; } @@ -309,13 +311,13 @@ _cb_messagetlvs(struct rfc5444_reader_tlvblock_context *context) { /* check if the topology information is recent enough */ if (_current.complete_tc) { - if (rfc5444_seqno_is_smaller(ansn, _current.node->ansn)) { + if (!seqno_reset && rfc5444_seqno_is_smaller(ansn, _current.node->ansn)) { OONF_DEBUG(LOG_OLSRV2_R, "ANSN %u is smaller than last stored ANSN %u", ansn, _current.node->ansn); return RFC5444_DROP_MSG_BUT_FORWARD; } } else { - if (!rfc5444_seqno_is_larger(ansn, _current.node->ansn)) { + if (!seqno_reset && !rfc5444_seqno_is_larger(ansn, _current.node->ansn)) { OONF_DEBUG(LOG_OLSRV2_R, "ANSN %u is smaller than last stored ANSN %u", ansn, _current.node->ansn); return RFC5444_DROP_MSG_BUT_FORWARD; } From 238c253b124ac241f0899c8e668131f0eeaa8cfe Mon Sep 17 00:00:00 2001 From: Henning Rogge Date: Fri, 11 Jun 2021 14:36:39 +0200 Subject: [PATCH 55/63] revert commit e1e49c0496be459f93e1508ff068ae5018622253 seems to trigger another problem, needs more work... --- include/oonf/base/oonf_duplicate_set.h | 5 +---- include/oonf/olsrv2/olsrv2/olsrv2.h | 2 +- src/base/oonf_duplicate_set.c | 2 +- src/olsrv2/olsrv2/olsrv2.c | 6 +----- src/olsrv2/olsrv2/olsrv2_reader.c | 8 +++----- 5 files changed, 7 insertions(+), 16 deletions(-) diff --git a/include/oonf/base/oonf_duplicate_set.h b/include/oonf/base/oonf_duplicate_set.h index b99ff7d9..06f08d87 100644 --- a/include/oonf/base/oonf_duplicate_set.h +++ b/include/oonf/base/oonf_duplicate_set.h @@ -86,9 +86,6 @@ enum oonf_duplicate_result /*! newer than the latest cached number */ OONF_DUPSET_NEWEST, - /*! sequence number was reset */ - OONF_DUPSET_RESET, - /*! sequence number was the first tested with the duplicate set */ OONF_DUPSET_FIRST, }; @@ -183,7 +180,7 @@ EXPORT const char *oonf_duplicate_get_result_str(enum oonf_duplicate_result); */ static INLINE bool oonf_duplicate_is_new(enum oonf_duplicate_result result) { - return result == OONF_DUPSET_NEW || result == OONF_DUPSET_NEWEST || result == OONF_DUPSET_RESET || result == OONF_DUPSET_FIRST; + return result == OONF_DUPSET_NEW || result == OONF_DUPSET_NEWEST || result == OONF_DUPSET_FIRST; } #endif /* OONF_DUPLICATE_SET_H_ */ diff --git a/include/oonf/olsrv2/olsrv2/olsrv2.h b/include/oonf/olsrv2/olsrv2/olsrv2.h index 153fa3fb..792a6d21 100644 --- a/include/oonf/olsrv2/olsrv2/olsrv2.h +++ b/include/oonf/olsrv2/olsrv2/olsrv2.h @@ -75,7 +75,7 @@ EXPORT uint64_t olsrv2_get_tc_interval(void); EXPORT uint64_t olsrv2_get_tc_validity(void); EXPORT bool olsrv2_is_nhdp_routable(struct netaddr *addr); EXPORT bool olsrv2_is_routable(struct netaddr *addr); -EXPORT bool olsrv2_mpr_shall_process(struct rfc5444_reader_tlvblock_context *, uint64_t vtime, bool *); +EXPORT bool olsrv2_mpr_shall_process(struct rfc5444_reader_tlvblock_context *, uint64_t vtime); EXPORT bool olsrv2_mpr_shall_forwarding( struct rfc5444_reader_tlvblock_context *context, struct netaddr *source_address, uint64_t vtime); EXPORT void olsrv2_generate_tcs(bool); diff --git a/src/base/oonf_duplicate_set.c b/src/base/oonf_duplicate_set.c index c1f978d2..48c061d8 100644 --- a/src/base/oonf_duplicate_set.c +++ b/src/base/oonf_duplicate_set.c @@ -319,7 +319,7 @@ _test(struct oonf_duplicate_set *dupset, struct oonf_duplicate_entry *entry, uin entry->too_old_count = 0; entry->current = seqno; - return OONF_DUPSET_RESET; + return OONF_DUPSET_NEWEST; } return OONF_DUPSET_TOO_OLD; } diff --git a/src/olsrv2/olsrv2/olsrv2.c b/src/olsrv2/olsrv2/olsrv2.c index e360293d..8530cc98 100644 --- a/src/olsrv2/olsrv2/olsrv2.c +++ b/src/olsrv2/olsrv2/olsrv2.c @@ -384,11 +384,10 @@ olsrv2_is_routable(struct netaddr *addr) { * to MPR settings. * @param context RFC5444 tlvblock reader context * @param vtime validity time for duplicate entry data - * @param reset pointer to boolean that will tell if the sequence number was reset * @return true if TC should be processed, false otherwise */ bool -olsrv2_mpr_shall_process(struct rfc5444_reader_tlvblock_context *context, uint64_t vtime, bool *reset) { +olsrv2_mpr_shall_process(struct rfc5444_reader_tlvblock_context *context, uint64_t vtime) { enum oonf_duplicate_result dup_result; bool process; #ifdef OONF_LOG_DEBUG_INFO @@ -408,9 +407,6 @@ olsrv2_mpr_shall_process(struct rfc5444_reader_tlvblock_context *context, uint64 dup_result = oonf_duplicate_entry_add(&_protocol->processed_set, context->msg_type, &context->orig_addr, context->seqno, vtime + _olsrv2_config.f_hold_time); process = oonf_duplicate_is_new(dup_result); - if (reset != NULL) { - *reset = dup_result == OONF_DUPSET_RESET; - } OONF_DEBUG(LOG_OLSRV2, "Do %sprocess message type %u from %s" diff --git a/src/olsrv2/olsrv2/olsrv2_reader.c b/src/olsrv2/olsrv2/olsrv2_reader.c index 8748ae26..1d8c80f5 100644 --- a/src/olsrv2/olsrv2/olsrv2_reader.c +++ b/src/olsrv2/olsrv2/olsrv2_reader.c @@ -215,7 +215,6 @@ _cb_messagetlvs(struct rfc5444_reader_tlvblock_context *context) { uint16_t ansn; uint8_t tmp; int af_type; - bool seqno_reset; #ifdef OONF_LOG_DEBUG_INFO struct netaddr_str buf; #endif @@ -296,8 +295,7 @@ _cb_messagetlvs(struct rfc5444_reader_tlvblock_context *context) { } /* test if we already processed the message */ - seqno_reset = false; - if (!olsrv2_mpr_shall_process(context, _current.vtime, &seqno_reset)) { + if (!olsrv2_mpr_shall_process(context, _current.vtime)) { OONF_DEBUG(LOG_OLSRV2_R, "Processing set says 'do not process'"); return RFC5444_DROP_MSG_BUT_FORWARD; } @@ -311,13 +309,13 @@ _cb_messagetlvs(struct rfc5444_reader_tlvblock_context *context) { /* check if the topology information is recent enough */ if (_current.complete_tc) { - if (!seqno_reset && rfc5444_seqno_is_smaller(ansn, _current.node->ansn)) { + if (rfc5444_seqno_is_smaller(ansn, _current.node->ansn)) { OONF_DEBUG(LOG_OLSRV2_R, "ANSN %u is smaller than last stored ANSN %u", ansn, _current.node->ansn); return RFC5444_DROP_MSG_BUT_FORWARD; } } else { - if (!seqno_reset && !rfc5444_seqno_is_larger(ansn, _current.node->ansn)) { + if (!rfc5444_seqno_is_larger(ansn, _current.node->ansn)) { OONF_DEBUG(LOG_OLSRV2_R, "ANSN %u is smaller than last stored ANSN %u", ansn, _current.node->ansn); return RFC5444_DROP_MSG_BUT_FORWARD; } From 7965956c7e9816e621b5c91e7cde086c7ef6b633 Mon Sep 17 00:00:00 2001 From: Henning Rogge Date: Wed, 18 Aug 2021 10:35:47 +0200 Subject: [PATCH 56/63] Allow double-slash prefix for auto-quit telnet commands without command chaining --- src/base/oonf_telnet.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/base/oonf_telnet.c b/src/base/oonf_telnet.c index cfa2eb60..628dd2a4 100644 --- a/src/base/oonf_telnet.c +++ b/src/base/oonf_telnet.c @@ -436,9 +436,16 @@ _cb_telnet_receive_data(struct oonf_stream_session *session) { /* handle difference between multicommand and singlecommand mode */ if (chainCommands) { - next = strchr(cmd, '/'); - if (next) { - *next++ = 0; + if (cmd[0] == '/') { + // double '/' means single command and autoquit + cmd++; + next = NULL; + } + else { + next = strchr(cmd, '/'); + if (next) { + *next++ = 0; + } } } para = strchr(cmd, ' '); From 6df2af0e1cb8c0219ff5ccdfb691c129027fa630 Mon Sep 17 00:00:00 2001 From: Henning Rogge Date: Wed, 18 Aug 2021 15:39:04 +0200 Subject: [PATCH 57/63] Add atomic replacement for origin to l2json import --- src/generic/layer2_json/layer2json.c | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/src/generic/layer2_json/layer2json.c b/src/generic/layer2_json/layer2json.c index 23e53d9e..d04853ce 100644 --- a/src/generic/layer2_json/layer2json.c +++ b/src/generic/layer2_json/layer2json.c @@ -135,6 +135,30 @@ _cb_layer2json(struct oonf_telnet_data *con) { return TELNET_RESULT_INTERNAL_ERROR; } } + if ((second = str_hasnextword(con->parameter, "replace"))) { + char originname[32]; + const char *third; + struct oonf_layer2_origin *origin; + + third = str_cpynextword(originname, second, sizeof(originname)); + if (!third) { + abuf_appendf(con->out, "Error, no origin provided"); + return TELNET_RESULT_ACTIVE; + } + + origin = oonf_layer2_origin_get(originname); + if (origin) { + struct oonf_layer2_net *l2net, *l2net_it; + + avl_for_each_element_safe(oonf_layer2_get_net_tree(), l2net, _node, l2net_it) { + oonf_layer2_net_remove(l2net, origin); + } + } + + if (l2json_import(con, third)) { + return TELNET_RESULT_INTERNAL_ERROR; + } + } return 0; } From fc1ee6414c47304b6847a99c42ef274f6273bdfb Mon Sep 17 00:00:00 2001 From: Henning Rogge Date: Tue, 28 Sep 2021 08:39:16 +0200 Subject: [PATCH 58/63] Fix some errors (most likely GCC changes) and disable DNS plugins for now --- CMakeLists.txt | 1 + apps/dlep-router/CMakeLists.txt | 4 ++-- include/oonf/generic/dlep/dlep_internal.h | 6 +++--- include/oonf/generic/nl80211_listener/nl80211_internal.h | 2 +- include/oonf/nhdp/mpr/mpr_internal.h | 2 +- include/oonf/nhdp/nhdp/nhdp_internal.h | 6 +++--- include/oonf/olsrv2/olsrv2/olsrv2_internal.h | 8 ++++---- src/generic/CMakeLists.txt | 4 ++-- 8 files changed, 17 insertions(+), 16 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index ea5182db..3cb8bec2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,5 @@ cmake_minimum_required(VERSION 2.8.12 FATAL_ERROR) +project (OONF C) ########################### #### API configuration #### diff --git a/apps/dlep-router/CMakeLists.txt b/apps/dlep-router/CMakeLists.txt index a205c70c..cc202029 100644 --- a/apps/dlep-router/CMakeLists.txt +++ b/apps/dlep-router/CMakeLists.txt @@ -49,8 +49,8 @@ IF (NOT OONF_STATIC_PLUGINS) remotecontrol layer2_config layer2_export - dns_query - dns_sd +# dns_query +# dns_sd dlep ) ENDIF (NOT OONF_STATIC_PLUGINS) diff --git a/include/oonf/generic/dlep/dlep_internal.h b/include/oonf/generic/dlep/dlep_internal.h index 1c553ee1..2a4278d8 100644 --- a/include/oonf/generic/dlep/dlep_internal.h +++ b/include/oonf/generic/dlep/dlep_internal.h @@ -49,13 +49,13 @@ #include /* headers only for use inside the generic DLEP code */ -enum oonf_log_source LOG_DLEP; +extern enum oonf_log_source LOG_DLEP; /* headers only for use inside the DLEP_RADIO code*/ -enum oonf_log_source LOG_DLEP_RADIO; +extern enum oonf_log_source LOG_DLEP_RADIO; /* headers only for use inside the DLEP_ROUTER code*/ -enum oonf_log_source LOG_DLEP_ROUTER; +extern enum oonf_log_source LOG_DLEP_ROUTER; #endif /* DLEP_INTERNAL_H_ */ diff --git a/include/oonf/generic/nl80211_listener/nl80211_internal.h b/include/oonf/generic/nl80211_listener/nl80211_internal.h index 38e1c5d4..afa8eb2d 100644 --- a/include/oonf/generic/nl80211_listener/nl80211_internal.h +++ b/include/oonf/generic/nl80211_listener/nl80211_internal.h @@ -49,6 +49,6 @@ #include /* headers only for use inside the NL80211 subsystem */ -enum oonf_log_source LOG_NL80211; +extern enum oonf_log_source LOG_NL80211; #endif /* NL80211_INTERNAL_H_ */ diff --git a/include/oonf/nhdp/mpr/mpr_internal.h b/include/oonf/nhdp/mpr/mpr_internal.h index 3cb92595..4021e0b2 100644 --- a/include/oonf/nhdp/mpr/mpr_internal.h +++ b/include/oonf/nhdp/mpr/mpr_internal.h @@ -48,6 +48,6 @@ #include /* headers only for use inside the MPR subsystem */ -enum oonf_log_source LOG_MPR; +extern enum oonf_log_source LOG_MPR; #endif /* MPR_INTERNAL_H_ */ diff --git a/include/oonf/nhdp/nhdp/nhdp_internal.h b/include/oonf/nhdp/nhdp/nhdp_internal.h index b20e10fc..e952c04f 100644 --- a/include/oonf/nhdp/nhdp/nhdp_internal.h +++ b/include/oonf/nhdp/nhdp/nhdp_internal.h @@ -49,8 +49,8 @@ #include /* headers only for use inside the NHDP subsystem */ -enum oonf_log_source LOG_NHDP; -enum oonf_log_source LOG_NHDP_R; -enum oonf_log_source LOG_NHDP_W; +extern enum oonf_log_source LOG_NHDP; +extern enum oonf_log_source LOG_NHDP_R; +extern enum oonf_log_source LOG_NHDP_W; #endif /* NHDP_INTERNAL_H_ */ diff --git a/include/oonf/olsrv2/olsrv2/olsrv2_internal.h b/include/oonf/olsrv2/olsrv2/olsrv2_internal.h index a43efec6..ea62e1a2 100644 --- a/include/oonf/olsrv2/olsrv2/olsrv2_internal.h +++ b/include/oonf/olsrv2/olsrv2/olsrv2_internal.h @@ -50,9 +50,9 @@ #include /* headers only for use inside the OLSRv2 subsystem */ -EXPORT enum oonf_log_source LOG_OLSRV2; -EXPORT enum oonf_log_source LOG_OLSRV2_R; -EXPORT enum oonf_log_source LOG_OLSRV2_ROUTING; -EXPORT enum oonf_log_source LOG_OLSRV2_W; +extern enum oonf_log_source LOG_OLSRV2; +extern enum oonf_log_source LOG_OLSRV2_R; +extern enum oonf_log_source LOG_OLSRV2_ROUTING; +extern enum oonf_log_source LOG_OLSRV2_W; #endif /* OLSRV2_INTERNAL_H_ */ diff --git a/src/generic/CMakeLists.txt b/src/generic/CMakeLists.txt index 9d085f8b..79c8244c 100644 --- a/src/generic/CMakeLists.txt +++ b/src/generic/CMakeLists.txt @@ -1,8 +1,8 @@ # add subdirectories add_subdirectory(cfg_compact) add_subdirectory(dlep) -add_subdirectory(dns_query) -add_subdirectory(dns_sd) +# add_subdirectory(dns_query) +# add_subdirectory(dns_sd) add_subdirectory(example) add_subdirectory(layer2info) add_subdirectory(layer2_import) From 919aea82f903a6a06deb75b3ee29b5a33b8a2c4c Mon Sep 17 00:00:00 2001 From: JeremiasHo <92977532+JeremiasHo@users.noreply.github.com> Date: Fri, 22 Oct 2021 10:44:41 +0200 Subject: [PATCH 59/63] ignore not yet implemented TLV 20 workaround to handle incoming DLEP messages with TLV 20 (MTU) by ignoring this optional data item without resulting in a session termination --- src/generic/dlep/dlep_session.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/generic/dlep/dlep_session.c b/src/generic/dlep/dlep_session.c index 6e40b899..24a2348d 100644 --- a/src/generic/dlep/dlep_session.c +++ b/src/generic/dlep/dlep_session.c @@ -919,6 +919,13 @@ _parse_tlvstream(struct dlep_session *session, const uint8_t *buffer, size_t len return DLEP_NEW_PARSER_INCOMPLETE_TLV; } + /* if TLV=20, we'll ignore it */ + if (tlv_type == 20) { + OONF_WARN(session->log_source, "ignore unsupported TLV 20"); + idx += tlv_length; + continue; + } + /* check if tlv is supported */ tlv = dlep_parser_get_tlv(parser, tlv_type); if (!tlv) { From 90900342eab0552317fce4bceba94ef32d1a9c6c Mon Sep 17 00:00:00 2001 From: JeremiasHo <92977532+JeremiasHo@users.noreply.github.com> Date: Fri, 22 Oct 2021 11:02:17 +0200 Subject: [PATCH 60/63] peer update fix for MTU TLV proposed fix by HRogge to avoid errors in PeerUpdate when receiving TLV 20 (MTU) --- src/generic/dlep/ext_base_metric/metric.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/generic/dlep/ext_base_metric/metric.c b/src/generic/dlep/ext_base_metric/metric.c index fd2d7ca3..66517266 100644 --- a/src/generic/dlep/ext_base_metric/metric.c +++ b/src/generic/dlep/ext_base_metric/metric.c @@ -85,6 +85,7 @@ static const uint16_t _peer_update_tlvs[] = { DLEP_RESOURCES_TLV, DLEP_RLQR_TLV, DLEP_RLQT_TLV, + DLEP_MTU_TLV, }; /* destination up/update */ From 3d60692415928a58b5b91f83aa33bf8c3f54e098 Mon Sep 17 00:00:00 2001 From: "Jakob (Jack/XDjackieXD)" Date: Sun, 5 Feb 2023 20:44:13 +0100 Subject: [PATCH 61/63] Fix #41: Use correct interface for establishing DLEP TCP session --- include/oonf/generic/dlep/dlep_session.h | 3 +++ src/generic/dlep/dlep_interface.c | 3 +++ src/generic/dlep/ext_base_proto/proto_router.c | 8 ++++---- 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/include/oonf/generic/dlep/dlep_session.h b/include/oonf/generic/dlep/dlep_session.h index 2c49c4dc..2bcc2bc1 100644 --- a/include/oonf/generic/dlep/dlep_session.h +++ b/include/oonf/generic/dlep/dlep_session.h @@ -356,6 +356,9 @@ struct dlep_session { /*! remember all streams bound to an interface */ struct avl_node _node; + + /*! interface index of the interface, the DLEP offer came from */ + unsigned dlep_if_index; }; void dlep_session_init(void); diff --git a/src/generic/dlep/dlep_interface.c b/src/generic/dlep/dlep_interface.c index 028cd600..2cccbec5 100644 --- a/src/generic/dlep/dlep_interface.c +++ b/src/generic/dlep/dlep_interface.c @@ -224,6 +224,9 @@ _cb_receive_udp(struct oonf_packet_socket *pkt, union netaddr_socket *from, void /* copy socket information */ memcpy(&interf->session.remote_socket, from, sizeof(interf->session.remote_socket)); + /* save the interface index of the interface, the DLEP session offer came from */ + interf->session.dlep_if_index = pkt->os_if->index; + processed = dlep_session_process_buffer(&interf->session, buffer, length, true); if (processed < 0) { /* Session is now most likely invalid */ diff --git a/src/generic/dlep/ext_base_proto/proto_router.c b/src/generic/dlep/ext_base_proto/proto_router.c index a958debe..06936ca4 100644 --- a/src/generic/dlep/ext_base_proto/proto_router.c +++ b/src/generic/dlep/ext_base_proto/proto_router.c @@ -288,7 +288,7 @@ _router_process_peer_offer(struct dlep_extension *ext __attribute__((unused)), s ip = os_interface_get_prefix_from_dst(&addr, ifdata); if (ip) { result = &ip->address; - netaddr_socket_init(&remote, &addr, port, ifdata->index); + netaddr_socket_init(&remote, &addr, port, session->dlep_if_index); } } value = dlep_session_get_next_tlv_value(session, value); @@ -308,7 +308,7 @@ _router_process_peer_offer(struct dlep_extension *ext __attribute__((unused)), s ip = os_interface_get_prefix_from_dst(&addr, ifdata); if (ip) { result = &ip->address; - netaddr_socket_init(&remote, &addr, port, ifdata->index); + netaddr_socket_init(&remote, &addr, port, session->dlep_if_index); } } value = dlep_session_get_next_tlv_value(session, value); @@ -324,11 +324,11 @@ _router_process_peer_offer(struct dlep_extension *ext __attribute__((unused)), s return DLEP_NEW_PARSER_INTERNAL_ERROR; } result = &ip->address; - netaddr_socket_init(&remote, &addr, port, ifdata->index); + netaddr_socket_init(&remote, &addr, port, session->dlep_if_index); } /* initialize session */ - netaddr_socket_init(&local, result, 0, ifdata->index); + netaddr_socket_init(&local, result, 0, session->dlep_if_index); router_if = dlep_router_get_by_layer2_if(ifdata->name); if (router_if && &router_if->interf.session == session) { From 04b2c9a0670a8086fd4acc4823ba65b999a75ea6 Mon Sep 17 00:00:00 2001 From: "Jakob (Jack/XDjackieXD)" Date: Sun, 5 Feb 2023 22:38:58 +0100 Subject: [PATCH 62/63] Fix openwrt buildscript plugins --- openwrt/oonf-dlep-proxy-git-debug/Makefile | 2 +- openwrt/oonf-dlep-proxy-git/Makefile | 2 +- openwrt/oonf-dlep-radio-git-debug/Makefile | 2 +- openwrt/oonf-dlep-radio-git/Makefile | 2 +- openwrt/oonf-olsrd2-git-debug/Makefile | 4 ++-- openwrt/oonf-olsrd2-git/Makefile | 4 ++-- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/openwrt/oonf-dlep-proxy-git-debug/Makefile b/openwrt/oonf-dlep-proxy-git-debug/Makefile index bdb6986a..89d7e42a 100644 --- a/openwrt/oonf-dlep-proxy-git-debug/Makefile +++ b/openwrt/oonf-dlep-proxy-git-debug/Makefile @@ -14,7 +14,7 @@ CMAKE_OPTIONS=-D OONF_NO_WERROR:Bool=true \ -D OONF_NO_TESTING:Bool=true \ -D UCI:Bool=true \ -D OONF_APP_DEFAULT_CFG_HANDLER:String=uci \ - -D OONF_STATIC_PLUGINS:String="class;clock;layer2;packet_socket;socket;stream_socket;telnet;timer;viewer;os_clock;os_fd;os_interface;os_system;nl80211_listener;layer2info;systeminfo;cfg_uciloader;cfg_compact;dlep_proxy" \ + -D OONF_STATIC_PLUGINS:String="class;callback;clock;layer2;packet_socket;socket;stream_socket;telnet;timer;viewer;os_clock;os_fd;os_interface;os_system;nl80211_listener;layer2info;systeminfo;cfg_uciloader;cfg_compact;dlep_proxy" \ -D INSTALL_LIB_DIR:Path=lib/oonf \ -D INSTALL_INCLUDE_DIR:Path=include/oonf \ -D INSTALL_CMAKE_DIR:Path=lib/oonf \ diff --git a/openwrt/oonf-dlep-proxy-git/Makefile b/openwrt/oonf-dlep-proxy-git/Makefile index 1cf4de79..91f35375 100644 --- a/openwrt/oonf-dlep-proxy-git/Makefile +++ b/openwrt/oonf-dlep-proxy-git/Makefile @@ -14,7 +14,7 @@ CMAKE_OPTIONS=-D OONF_NO_WERROR:Bool=true \ -D OONF_NO_TESTING:Bool=true \ -D UCI:Bool=true \ -D OONF_APP_DEFAULT_CFG_HANDLER:String=uci \ - -D OONF_STATIC_PLUGINS:String="class;clock;layer2;packet_socket;socket;stream_socket;telnet;timer;viewer;os_clock;os_fd;os_interface;os_system;nl80211_listener;layer2info;systeminfo;cfg_uciloader;cfg_compact;dlep_proxy" \ + -D OONF_STATIC_PLUGINS:String="class;callback;clock;layer2;packet_socket;socket;stream_socket;telnet;timer;viewer;os_clock;os_fd;os_interface;os_system;nl80211_listener;layer2info;systeminfo;cfg_uciloader;cfg_compact;dlep_proxy" \ -D INSTALL_LIB_DIR:Path=lib/oonf \ -D INSTALL_INCLUDE_DIR:Path=include/oonf \ -D INSTALL_CMAKE_DIR:Path=lib/oonf \ diff --git a/openwrt/oonf-dlep-radio-git-debug/Makefile b/openwrt/oonf-dlep-radio-git-debug/Makefile index 3790fb3b..d6d67dd3 100644 --- a/openwrt/oonf-dlep-radio-git-debug/Makefile +++ b/openwrt/oonf-dlep-radio-git-debug/Makefile @@ -14,7 +14,7 @@ CMAKE_OPTIONS=-D OONF_NO_WERROR:Bool=true \ -D OONF_NO_TESTING:Bool=true \ -D UCI:Bool=true \ -D OONF_APP_DEFAULT_CFG_HANDLER:String=uci \ - -D OONF_STATIC_PLUGINS:String="class;clock;layer2;packet_socket;socket;stream_socket;telnet;timer;viewer;os_clock;os_fd;os_interface;os_system;nl80211_listener;layer2info;systeminfo;cfg_uciloader;cfg_compact;dlep_radio" \ + -D OONF_STATIC_PLUGINS:String="class;callback;clock;layer2;packet_socket;socket;stream_socket;telnet;timer;viewer;os_clock;os_fd;os_interface;os_system;nl80211_listener;layer2info;systeminfo;cfg_uciloader;cfg_compact;dlep" \ -D INSTALL_LIB_DIR:Path=lib/oonf \ -D INSTALL_INCLUDE_DIR:Path=include/oonf \ -D INSTALL_CMAKE_DIR:Path=lib/oonf \ diff --git a/openwrt/oonf-dlep-radio-git/Makefile b/openwrt/oonf-dlep-radio-git/Makefile index ff03ddd5..f3eb427c 100644 --- a/openwrt/oonf-dlep-radio-git/Makefile +++ b/openwrt/oonf-dlep-radio-git/Makefile @@ -14,7 +14,7 @@ CMAKE_OPTIONS=-D OONF_NO_WERROR:Bool=true \ -D OONF_NO_TESTING:Bool=true \ -D UCI:Bool=true \ -D OONF_APP_DEFAULT_CFG_HANDLER:String=uci \ - -D OONF_STATIC_PLUGINS:String="class;clock;layer2;packet_socket;socket;stream_socket;telnet;timer;viewer;os_clock;os_fd;os_interface;os_system;nl80211_listener;layer2info;systeminfo;cfg_uciloader;cfg_compact;dlep_radio" \ + -D OONF_STATIC_PLUGINS:String="class;callback;clock;layer2;packet_socket;socket;stream_socket;telnet;timer;viewer;os_clock;os_fd;os_interface;os_system;nl80211_listener;layer2info;systeminfo;cfg_uciloader;cfg_compact;dlep" \ -D INSTALL_LIB_DIR:Path=lib/oonf \ -D INSTALL_INCLUDE_DIR:Path=include/oonf \ -D INSTALL_CMAKE_DIR:Path=lib/oonf \ diff --git a/openwrt/oonf-olsrd2-git-debug/Makefile b/openwrt/oonf-olsrd2-git-debug/Makefile index 587c2da0..68e4f8d2 100644 --- a/openwrt/oonf-olsrd2-git-debug/Makefile +++ b/openwrt/oonf-olsrd2-git-debug/Makefile @@ -14,7 +14,7 @@ CMAKE_OPTIONAL_PLUGINS:= $(subst $(SPACE),;,$(strip \ $(if $(filter y,$(CONFIG_OONF_NHDP_AUTOLL4)),auto_ll4,) \ $(if $(filter y,$(CONFIG_OONF_OLSRV2_LAN_IMPORT)),lan_import,) \ $(if $(filter y,$(CONFIG_OONF_OLSRV2_ROUTE_MODIFIER)),route_modifier,) \ - $(if $(filter y,$(CONFIG_OONF_GENERIC_DLEP_ROUTER)),dlep_router,) \ + $(if $(filter y,$(CONFIG_OONF_GENERIC_DLEP_ROUTER)),dlep,) \ $(if $(filter y,$(CONFIG_OONF_GENERIC_REMOTECONTROL)),remotecontrol,) \ $(if $(filter y,$(CONFIG_OONF_OLSRV2_MPR)),mpr,) \ $(if $(filter y,$(CONFIG_OONF_GENERIC_HTTP)),http,) \ @@ -29,7 +29,7 @@ CMAKE_OPTIONS=-D CMAKE_BUILD_TYPE:String=Debug \ -D OONF_NO_TESTING:Bool=true \ -D UCI:Bool=true \ -D OONF_APP_DEFAULT_CFG_HANDLER:String=uci \ - -D OONF_STATIC_PLUGINS:String="class;clock;duplicate_set;layer2;packet_socket;rfc5444;socket;stream_socket;telnet;timer;viewer;os_clock;os_fd;os_interface;os_routing;os_system;nhdp;olsrv2;ff_dat_metric;neighbor_probing;nl80211_listener;link_config;layer2info;systeminfo;cfg_uciloader;cfg_compact;nhdpinfo;olsrv2info;netjsoninfo;${CMAKE_OPTIONAL_PLUGINS}" \ + -D OONF_STATIC_PLUGINS:String="class;callback;clock;duplicate_set;layer2;packet_socket;rfc5444;socket;stream_socket;telnet;timer;viewer;os_clock;os_fd;os_interface;os_routing;os_system;nhdp;olsrv2;ff_dat_metric;neighbor_probing;nl80211_listener;link_config;layer2info;systeminfo;cfg_uciloader;cfg_compact;nhdpinfo;olsrv2info;netjsoninfo;${CMAKE_OPTIONAL_PLUGINS}" \ -D OONF_LIB_GIT:String=v$(PKG_VERSION)-archive \ -D OONF_VERSION:String=$(PKG_VERSION) \ -D INSTALL_LIB_DIR:Path=lib/oonf \ diff --git a/openwrt/oonf-olsrd2-git/Makefile b/openwrt/oonf-olsrd2-git/Makefile index 21514582..2b37fd7b 100644 --- a/openwrt/oonf-olsrd2-git/Makefile +++ b/openwrt/oonf-olsrd2-git/Makefile @@ -14,7 +14,7 @@ CMAKE_OPTIONAL_PLUGINS:= $(subst $(SPACE),;,$(strip \ $(if $(filter y,$(CONFIG_OONF_NHDP_AUTOLL4)),auto_ll4,) \ $(if $(filter y,$(CONFIG_OONF_OLSRV2_LAN_IMPORT)),lan_import,) \ $(if $(filter y,$(CONFIG_OONF_OLSRV2_ROUTE_MODIFIER)),route_modifier,) \ - $(if $(filter y,$(CONFIG_OONF_GENERIC_DLEP_ROUTER)),dlep_router,) \ + $(if $(filter y,$(CONFIG_OONF_GENERIC_DLEP_ROUTER)),dlep,) \ $(if $(filter y,$(CONFIG_OONF_GENERIC_REMOTECONTROL)),remotecontrol,) \ $(if $(filter y,$(CONFIG_OONF_OLSRV2_MPR)),mpr,) \ $(if $(filter y,$(CONFIG_OONF_GENERIC_HTTP)),http,) \ @@ -31,7 +31,7 @@ CMAKE_OPTIONS=-D CMAKE_BUILD_TYPE:String=$(BUILD_TYPE) \ -D OONF_NO_TESTING:Bool=true \ -D UCI:Bool=true \ -D OONF_APP_DEFAULT_CFG_HANDLER:String=uci \ - -D OONF_STATIC_PLUGINS:String="class;clock;duplicate_set;layer2;packet_socket;rfc5444;socket;stream_socket;telnet;timer;viewer;os_clock;os_fd;os_interface;os_routing;os_system;nhdp;olsrv2;ff_dat_metric;neighbor_probing;nl80211_listener;link_config;layer2info;systeminfo;cfg_uciloader;cfg_compact;nhdpinfo;olsrv2info;netjsoninfo;${CMAKE_OPTIONAL_PLUGINS}" \ + -D OONF_STATIC_PLUGINS:String="class;callback;clock;duplicate_set;layer2;packet_socket;rfc5444;socket;stream_socket;telnet;timer;viewer;os_clock;os_fd;os_interface;os_routing;os_system;nhdp;olsrv2;ff_dat_metric;neighbor_probing;nl80211_listener;link_config;layer2info;systeminfo;cfg_uciloader;cfg_compact;nhdpinfo;olsrv2info;netjsoninfo;${CMAKE_OPTIONAL_PLUGINS}" \ -D OONF_LIB_GIT:String=v$(PKG_VERSION)-archive \ -D OONF_VERSION:String=$(PKG_VERSION) \ -D INSTALL_LIB_DIR:Path=lib/oonf \ From fbea183731a4928454e8c91a90b5ba0f4968fb42 Mon Sep 17 00:00:00 2001 From: "Jakob (Jack/XDjackieXD)" Date: Mon, 6 Mar 2023 21:39:12 +0100 Subject: [PATCH 63/63] Fix lan_import plugin build --- src/olsrv2/CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/src/olsrv2/CMakeLists.txt b/src/olsrv2/CMakeLists.txt index 2a676952..a7bcece3 100644 --- a/src/olsrv2/CMakeLists.txt +++ b/src/olsrv2/CMakeLists.txt @@ -6,4 +6,5 @@ add_subdirectory(olsrv2_old_lan) add_subdirectory(olsrv2_l2import) add_subdirectory(olsrv2_lan) add_subdirectory(route_modifier) +add_subdirectory(lan_import)