Commit 1361ede0 authored by Beniamino Galvani's avatar Beniamino Galvani

Squashed 'shared/n-acd/' changes from a68b55992..a40949267

a40949267 build: add CI run without ebpf
044db2056 n-acd: drop redundant headers
6a391cd83 n-acd: fix build without eBPF
bb194cf09 n-acd/config: make transport mandatory
ec2865743 build: drop unused c-sundry
721d9d84f n-acd: inline c_container_of()
1a7ee317c util/timer: fix coding-style
6c96f926b util/timer: fall back to CLOCK_MONOTONIC if necessary
4ea3165fc n-acd: only use CLOCK_BOOTTIME if really necessary
c1b853c6c util/timer: cleanup headers
b1d6ad272 n-acd: add destructors that return void
185be55b6 test-bpf: skip test in case of unsufficient privs
84a40e8fa build: add NEWS file
bf11443ff build: mention mailinglist in readme
e2797984a test-bpf: drop bpf-filter.h
668ed3c82 subprojects: pull in updates
dd8cab3f0 test-veth: reduce parallel execution to 9
68b09ba2b build: update AUTHORS
3f77e3e88 test: make function headers valid C
5275a5120 test: get rid of spurious tab
037df412c n-acd: make struct initializers valid C
346ec0c67 build: upgrade CI
38682a36d n-acd: fix signed vs unsigned comparison
5e7578b33 bpf: properly zero out trailing bpf_attr space
ee1e432ae probe: fix coding-style
a143540f9 build: use lower-case build options
835533e7d build: minor style fixes
2bd6d1d29 build: get rid of tabs
b14979934 eBPF: make compile-time optional
6f13c27ee n-acd: filter out invalid packets
4e6a169a0 build: sync with c-util repositories
6c4a9117b build: document eBPF kernel requirement
3ef08394d n-acd: don't remember dropped defense attempts
4dff8771f n-acd: fix coding-style
b11fb9706 n-acd/config: default to the RFC-specified timeout
d885bb3b7 n-acd/event: don't expose the type of operation that caused a conflict
e2f87e047 TODO: drop remaining items
f06993856 test/veth: reduce the number of probed addresses
8b4f7ed64 test/veth: bump the timeout a bit
14e4606f6 n-acd/probe: don't cap the jitter at 4s
a0247b86f test/veth: fix stackvariable corruption
a64ac8389 n-acd/probe: update comments
aa9c25bc1 n-acd/handle_timeout: update comments
b6c2df3a9 timer: rename timer_pop() to timer_pop_timeout()
47c657a8d test: fix handling of child addresses
27168ba9e timer: move timer_read() from n-acd.c to util/timer.c
21a1e37aa timer: require timer to be explicitly rearmed
ee1080820 bpf/map: make key/value sizes self-documenting
fd444353e test/veth: rework test
ba2bc433c test: rework child_ip() helper
07881b8da test: silence a warning
38da00b0a test/bpf: make tests for map modifications more comprehensive
6a2ffd23a test/timerfd: for documentation purposes verify the kernel API
01a9cf54b probe: move from ms to ns internally
4fe438dd9 n-acd: move to use the Timer utility library
e098cfc79 util: add a timer utility helper
8ea196e5b subprojects: pull in c-sundry
0c0b3c29f acd/probe: do not subscribe to packets in FAILED state
9c922ea3d acd/probe: introduce probe_{un,}link() helpers
024a830e6 acd/probe: use unschedule() helper in free()
b098a3bcc tests/veth: minor fixes to the test
fe3d9578a acd/packet: consider unexpected packets a fatal error
34d7656d7 acd: stop state-machine after USED or CONFLICT events
7d9e5ec6b acd: don't declare iovec entries inline
7afd8d8a3 tests: add veth test
26a737b42 tests/veth: add helper for adding IP addresses to child device
e73a37a11 probe: store a userdata pointer in the probe object
327e82625 test: introduce loopback helper
0682b15f8 acd: reduce default map size
afead881f tests: reinstate loopback test
4527d2f71 BPF: move and document the eBPF helpers
88bacc022 socket filter: move to the new eBPF helpers
245104d5c tests: skip tests if lacking permissions
195d9ff5a n-acd: rework API to support many probes on a context
ab440eb99 eBPF: never return packets that userspace should unconditionally drop
ac933f412 eBPF: add eBPF helper functions

git-subtree-dir: shared/n-acd
git-subtree-split: a40949267923c45cb232fa4c1d60eafacee4b36e
parent 23cbce4b
#!/bin/bash
set -e
rm -Rf "./ci-build"
mkdir "./ci-build"
cd "./ci-build"
${CHERRY_LIB_MESONSETUP} . "${CHERRY_LIB_SRCDIR}" ${N_ACD_CONF}
${CHERRY_LIB_NINJABUILD}
sudo ${CHERRY_LIB_MESONTEST}
# no valgrind tests, since bpf(2) is not supported by it
#!/bin/bash
set -e
CHERRY_MATRIX+=("export N_ACD_CONF=-Debpf=false ${CHERRY_LIB_M_DEFAULT[*]}")
# http://EditorConfig.org
# top-most EditorConfig file
root = true
# Unix-style newlines with a newline ending every file, utf-8 charset
[*]
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true
charset = utf-8
# match config files, set indent to spaces with width of eight
[*.{c,h}]
indent_style = space
indent_size = 8
......@@ -4,3 +4,6 @@
[submodule "subprojects/c-siphash"]
path = subprojects/c-siphash
url = https://github.com/c-util/c-siphash.git
[submodule "subprojects/c-rbtree"]
path = subprojects/c-rbtree
url = https://github.com/c-util/c-rbtree.git
dist: trusty
sudo: required
os: linux
dist: trusty
language: c
compiler:
- gcc
- clang
install:
- curl -L "https://github.com/ninja-build/ninja/releases/download/v1.7.2/ninja-linux.zip" -o "ninja-linux.zip"
- sudo unzip "ninja-linux.zip" -d "/usr/local/bin"
- sudo chmod 755 "/usr/local/bin/ninja"
- pip3 install meson
services:
- docker
before_install:
- curl -O -L "https://raw.githubusercontent.com/cherry-pick/cherry-images/v1/scripts/vmrun"
- curl -O -L "https://raw.githubusercontent.com/cherry-pick/cherry-ci/v1/scripts/cherryci"
- chmod +x "./vmrun" "./cherryci"
script:
- meson "build"
- ninja -C "build"
- sudo MESON_TESTTHREADS=64 ninja -C "build" test
jobs:
include:
- stage: test
script:
- ./vmrun -- ../src/cherryci -d ../src/.cherryci -s c-util -m
- script:
- ./vmrun -T armv7hl -- ../src/cherryci -d ../src/.cherryci -s c-util
- script:
- ./vmrun -T i686 -- ../src/cherryci -d ../src/.cherryci -s c-util
LICENSE:
This project is dual-licensed under both the Apache License, Version
2.0, and the GNU Lesser General Public License, Version 2.1+.
AUTHORS-ASL:
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
......@@ -11,9 +15,25 @@ LICENSE:
See the License for the specific language governing permissions and
limitations under the License.
AUTHORS-LGPL:
This program is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published
by the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program; If not, see <http://www.gnu.org/licenses/>.
COPYRIGHT: (ordered alphabetically)
Copyright (C) 2015-2017 Red Hat, Inc.
Copyright (C) 2015-2018 Red Hat, Inc.
AUTHORS: (ordered alphabetically)
Beniamino Galvani <bgalvani@redhat.com>
David Herrmann <dh.herrmann@gmail.com>
Thomas Haller <thaller@redhat.com>
Tom Gundersen <teg@jklm.no>
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
AUTHORS-ASL
\ No newline at end of file
n-acd - IPv4 Address Conflict Detection
CHANGES WITH 1:
* Initial release of n-acd. This project implements the IPv4 Address
Conflict Detection standard as defined in RFC-5227. The state machine
is implemented in a shared library and provides a stable ISO-C11 API.
The implementation is linux-only and relies heavily on the API
behavior of recent linux kernel releases.
* Compared to the pre-releases, this release supports many parallel
probes on a single n-acd context. This reduces the number of
allocated network resources to O(1), based on the number of running
parallel probes.
* The n-acd project is now dual-licensed: ASL-2.0 and LGPL-2.1+
Contributions from: Beniamino Galvani, David Herrmann, Thomas Haller,
Tom Gundersen
- Tübingen, 2018-08-08
......@@ -8,7 +8,7 @@ ABOUT:
kernel releases.
DETAILS:
https://github.com/nettools/n-acd/wiki
https://nettools.github.io/n-acd
BUG REPORTS:
https://github.com/nettools/n-acd/issues
......@@ -20,14 +20,18 @@ GIT:
GITWEB:
https://github.com/nettools/n-acd
MAILINGLIST:
https://groups.google.com/forum/#!forum/nettools-devel
LICENSE:
Apache Software License 2.0 (LICENSE)
See COPYING for details.
Apache Software License 2.0
Lesser General Public License 2.1+
See AUTHORS for details.
REQUIREMENTS:
The requirements for n-acd are:
Linux kernel >= 3.0
Linux kernel >= 3.19
libc (e.g., glibc >= 2.16)
At build-time, the following software is required:
......@@ -36,15 +40,15 @@ REQUIREMENTS:
pkg-config >= 0.29
INSTALL:
The meson build-system is used for n-acd. Contact upstream
The meson build-system is used for this project. Contact upstream
documentation for detailed help. In most situations the following
commands are sufficient to build and install n-acd from source:
commands are sufficient to build and install from source:
$ mkdir build
$ cd build
$ meson setup . ..
$ meson setup ..
$ ninja
$ ninja test
$ meson test
# ninja install
No custom configuration options are available.
For custom configuration options see meson_options.txt.
project('n-acd',
project(
'n-acd',
'c',
version: '1',
license: 'Apache',
default_options: [
'buildtype=release',
'c_std=c11',
])
],
)
project_description = 'IPv4 Address Conflict Detection'
add_project_arguments('-D_GNU_SOURCE', language: 'c')
mod_pkgconfig = import('pkgconfig')
sub_clist = subproject('c-list')
sub_crbtree = subproject('c-rbtree')
sub_csiphash = subproject('c-siphash')
dep_clist = sub_clist.get_variable('libclist_dep')
dep_crbtree = sub_crbtree.get_variable('libcrbtree_dep')
dep_csiphash = sub_csiphash.get_variable('libcsiphash_dep')
use_ebpf = get_option('ebpf')
subdir('src')
option('ebpf', type: 'boolean', value: true, description: 'Enable eBPF packet filtering')
LIBNACD_1 {
LIBNACD_2 {
global:
n_acd_config_new;
n_acd_config_free;
n_acd_config_set_ifindex;
n_acd_config_set_transport;
n_acd_config_set_mac;
n_acd_probe_config_new;
n_acd_probe_config_free;
n_acd_probe_config_set_ip;
n_acd_probe_config_set_timeout;
n_acd_new;
n_acd_free;
n_acd_ref;
n_acd_unref;
n_acd_get_fd;
n_acd_dispatch;
n_acd_pop_event;
n_acd_start;
n_acd_stop;
n_acd_announce;
n_acd_probe;
n_acd_probe_free;
n_acd_probe_set_userdata;
n_acd_probe_get_userdata;
n_acd_probe_announce;
local:
*;
};
#
# target: libnacd.so
# We build both, a static and a shared library. We want our tests to get access
# to internals, so we link them statically.
#
libnacd_private = static_library('nacd-private',
['n-acd.c'],
c_args: [
'-fvisibility=hidden',
'-fno-common'
],
dependencies: [
dep_clist,
dep_csiphash,
],
pic: true)
install_headers('n-acd.h')
libnacd_symfile = join_paths(meson.current_source_dir(), 'libnacd.sym')
libnacd_shared = shared_library('nacd',
dependencies: dep_csiphash,
objects: libnacd_private.extract_all_objects(),
install: true,
soversion: 0,
link_depends: libnacd_symfile,
link_args: [
'-Wl,--no-undefined',
'-Wl,--version-script=@0@'.format(libnacd_symfile)
])
mod_pkgconfig.generate(libraries: libnacd_shared,
version: meson.project_version(),
name: 'libnacd',
filebase: 'libnacd',
description: 'IPv4 Address Conflict Detection')
#
# target: test-api
# The test-api program explicitly links against the shared library, since it
# tests for symbol visibility.
#
libnacd_deps = [
dep_clist,
dep_crbtree,
dep_csiphash,
]
test_api = executable('test-api',
['test-api.c'],
link_with: libnacd_shared)
test('API Symbol Visibility', test_api)
libnacd_sources = [
'n-acd.c',
'n-acd-probe.c',
'util/timer.c',
]
if use_ebpf
libnacd_sources += [
'n-acd-bpf.c',
]
else
libnacd_sources += [
'n-acd-bpf-fallback.c',
]
endif
libnacd_private = static_library(
'nacd-private',
libnacd_sources,
c_args: [
'-fvisibility=hidden',
'-fno-common'
],
dependencies: libnacd_deps,
pic: true,
)
libnacd_shared = shared_library(
'nacd',
objects: libnacd_private.extract_all_objects(),
dependencies: libnacd_deps,
install: not meson.is_subproject(),
soversion: 0,
link_depends: libnacd_symfile,
link_args: [
'-Wl,--no-undefined',
'-Wl,--version-script=@0@'.format(libnacd_symfile)
],
)
libnacd_dep = declare_dependency(
include_directories: include_directories('.'),
link_with: libnacd_private,
dependencies: libnacd_deps,
version: meson.project_version(),
)
if not meson.is_subproject()
install_headers('n-acd.h')
mod_pkgconfig.generate(
libraries: libnacd_shared,
version: meson.project_version(),
name: 'libnacd',
filebase: 'libnacd',
description: project_description,
)
endif
#
# target: test-*
# All other tests are listed here. They link against the static library, so
# they can access internals for verification.
#
test_basic = executable('test-basic',
['test-basic.c'],
link_with: libnacd_private)
test('Basic API Behavior', test_basic)
test_api = executable('test-api', ['test-api.c'], link_with: libnacd_shared)
test('API Symbol Visibility', test_api)
if use_ebpf
test_bpf = executable('test-bpf', ['test-bpf.c'], dependencies: libnacd_dep)
test('eBPF socket filtering', test_bpf)
endif
test_loopback = executable('test-loopback',
['test-loopback.c'],
link_with: libnacd_private)
test_loopback = executable('test-loopback', ['test-loopback.c'], dependencies: libnacd_dep)
test('Echo Suppression via Loopback', test_loopback)
test_twice = executable('test-twice',
['test-twice.c'],
link_with: libnacd_private)
test('Two ACD in Parallel', test_twice)
test_timer = executable('test-timer', ['util/test-timer.c'], dependencies: libnacd_dep)
test('Timer helper', test_timer)
test_unplug = executable('test-unplug',
['test-unplug.c'],
link_with: libnacd_private)
test('Async Interface Hotplug', test_unplug)
#test_unplug = executable('test-unplug', ['test-unplug.c'], dependencies: libnacd_dep)
#test('Async Interface Hotplug', test_unplug)
test_unused = executable('test-unsed',
['test-unused.c'],
link_with: libnacd_private)
test('Unconflicted ACD', test_unused)
test_veth = executable('test-veth', ['test-veth.c'], dependencies: libnacd_dep)
test('Parallel ACD instances', test_veth)
/*
* A noop implementation of eBPF filter for IPv4 Address Conflict Detection
*
* These are a collection of dummy funcitons that have no effect, but allows
* n-acd to compile without eBPF support.
*
* See n-acd-bpf.c for documentation.
*/
#include <stddef.h>
#include "n-acd-private.h"
int n_acd_bpf_map_create(int *mapfdp, size_t max_entries) {
*mapfdp = -1;
return 0;
}
int n_acd_bpf_map_add(int mapfd, struct in_addr *addrp) {
return 0;
}
int n_acd_bpf_map_remove(int mapfd, struct in_addr *addrp) {
return 0;
}
int n_acd_bpf_compile(int *progfdp, int mapfd, struct ether_addr *macp) {
*progfdp = -1;
return 0;
}
This diff is collapsed.
#pragma once
#include <c-list.h>
#include <c-rbtree.h>
#include <errno.h>
#include <inttypes.h>
#include <netinet/if_ether.h>
#include <netinet/in.h>
#include <stdbool.h>
#include <stdlib.h>
#include "util/timer.h"
#include "n-acd.h"
typedef struct NAcdEventNode NAcdEventNode;
#define _cleanup_(_x) __attribute__((__cleanup__(_x)))
#define _public_ __attribute__((__visibility__("default")))
/* This augments the error-codes with internal ones that are never exposed. */
enum {
_N_ACD_INTERNAL = _N_ACD_E_N,
N_ACD_E_DROPPED,
};
enum {
N_ACD_PROBE_STATE_PROBING,
N_ACD_PROBE_STATE_CONFIGURING,
N_ACD_PROBE_STATE_ANNOUNCING,
N_ACD_PROBE_STATE_FAILED,
};
struct NAcdConfig {
int ifindex;
unsigned int transport;
uint8_t mac[ETH_ALEN];
size_t n_mac;
};
#define N_ACD_CONFIG_NULL(_x) { \
.transport = _N_ACD_TRANSPORT_N, \
}
struct NAcdProbeConfig {
struct in_addr ip;
uint64_t timeout_msecs;
};
#define N_ACD_PROBE_CONFIG_NULL(_x) { \
.timeout_msecs = N_ACD_TIMEOUT_RFC5227, \
}
struct NAcdEventNode {
CList acd_link;
CList probe_link;
NAcdEvent event;
uint8_t sender[ETH_ALEN];
bool is_public : 1;
};
#define N_ACD_EVENT_NODE_NULL(_x) { \
.acd_link = C_LIST_INIT((_x).acd_link), \
.probe_link = C_LIST_INIT((_x).probe_link), \
}
struct NAcd {
unsigned long n_refs;
unsigned int seed;
int fd_epoll;
int fd_socket;
CRBTree ip_tree;
CList event_list;
Timer timer;
/* BPF map */
int fd_bpf_map;
size_t n_bpf_map;
size_t max_bpf_map;
/* configuration */
int ifindex;
uint8_t mac[ETH_ALEN];
/* flags */
bool preempted : 1;
};
#define N_ACD_NULL(_x) { \
.n_refs = 1, \
.fd_epoll = -1, \
.fd_socket = -1, \
.ip_tree = C_RBTREE_INIT, \
.event_list = C_LIST_INIT((_x).event_list), \
.timer = TIMER_NULL((_x).timer), \
.fd_bpf_map = -1, \
}
struct NAcdProbe {
NAcd *acd;
CRBNode ip_node;
CList event_list;
Timeout timeout;
/* configuration */
struct in_addr ip;
uint64_t timeout_multiplier;
void *userdata;
/* state */
unsigned int state;
unsigned int n_iteration;
unsigned int defend;
uint64_t last_defend;
};
#define N_ACD_PROBE_NULL(_x) { \
.ip_node = C_RBNODE_INIT((_x).ip_node), \
.event_list = C_LIST_INIT((_x).event_list), \
.timeout = TIMEOUT_INIT((_x).timeout), \
.state = N_ACD_PROBE_STATE_PROBING, \
.defend = N_ACD_DEFEND_NEVER, \
}
/* events */
int n_acd_event_node_new(NAcdEventNode **nodep);
NAcdEventNode *n_acd_event_node_free(NAcdEventNode *node);
/* contexts */
void n_acd_remember(NAcd *acd, uint64_t now, bool success);
int n_acd_raise(NAcd *acd, NAcdEventNode **nodep, unsigned int event);
int n_acd_send(NAcd *acd, const struct in_addr *tpa, const struct in_addr *spa);
int n_acd_ensure_bpf_map_space(NAcd *acd);
/* probes */
int n_acd_probe_new(NAcdProbe **probep, NAcd *acd, NAcdProbeConfig *config);
int n_acd_probe_raise(NAcdProbe *probe, NAcdEventNode **nodep, unsigned int event);
int n_acd_probe_handle_timeout(NAcdProbe *probe);
int n_acd_probe_handle_packet(NAcdProbe *probe, struct ether_arp *packet, bool hard_conflict);
/* eBPF */
int n_acd_bpf_map_create(int *mapfdp, size_t max_elements);
int n_acd_bpf_map_add(int mapfd, struct in_addr *addr);
int n_acd_bpf_map_remove(int mapfd, struct in_addr *addr);
int n_acd_bpf_compile(int *progfdp, int mapfd, struct ether_addr *mac);
/* inline helpers */
static inline int n_acd_errno(void) {
/*
* Compilers continuously warn about uninitialized variables since they
* cannot deduce that `return -errno;` will always be negative. This
* small wrapper makes sure compilers figure that out. Use it as
* replacement for `errno` read access. Yes, it generates worse code,
* but only marginally and only affects slow-paths.
*/
return abs(errno) ? : EIO;
}
static inline void n_acd_event_node_freep(NAcdEventNode **node) {
if (*node)
n_acd_event_node_free(*node);
}
static inline void n_acd_closep(int *fdp) {
if (*fdp >= 0)
close(*fdp);
}
This diff is collapsed.
This diff is collapsed.
......@@ -15,41 +15,22 @@ extern "C" {
#include <netinet/in.h>
#include <stdbool.h>
typedef struct NAcd NAcd;
typedef struct NAcdConfig NAcdConfig;
typedef struct NAcdEvent NAcdEvent;
typedef struct NAcdProbe NAcdProbe;
typedef struct NAcdProbeConfig NAcdProbeConfig;
#define N_ACD_TIMEOUT_RFC5227 (UINT64_C(9000))
enum {
_N_ACD_E_SUCCESS,
N_ACD_E_DONE,
N_ACD_E_STOPPED,
N_ACD_E_PREEMPTED,
N_ACD_E_INVALID_ARGUMENT,
N_ACD_E_BUSY,
};
typedef struct NAcd NAcd;
typedef struct NAcdConfig {
int ifindex;
unsigned int transport;
const uint8_t *mac;
size_t n_mac;
struct in_addr ip;
uint64_t timeout_msec;
} NAcdConfig;
typedef struct NAcdEvent {
unsigned int event;
union {
struct {
} ready, down;
struct {
uint16_t operation;
uint8_t *sender;
size_t n_sender;
struct in_addr target;
} used, defended, conflict;
};
} NAcdEvent;
_N_ACD_E_N,
};
enum {
N_ACD_TRANSPORT_ETHERNET,
......@@ -72,21 +53,94 @@ enum {
_N_ACD_DEFEND_N,
};
int n_acd_new(NAcd **acdp);
NAcd *n_acd_free(NAcd *acd);
struct NAcdEvent {
unsigned int event;
union {
struct {
NAcdProbe *probe;
} ready;
struct {
} down;
struct {
NAcdProbe *probe;
uint8_t *sender;
size_t n_sender;
} used, defended, conflict;
};
};
/* configs */
void n_acd_get_fd(NAcd *acd, int *fdp);
int n_acd_config_new(NAcdConfig **configp);
NAcdConfig *n_acd_config_free(NAcdConfig *config);
void n_acd_config_set_ifindex(NAcdConfig *config, int ifindex);
void n_acd_config_set_transport(NAcdConfig *config, unsigned int transport);
void n_acd_config_set_mac(NAcdConfig *config, const uint8_t *mac, size_t n_mac);
int n_acd_probe_config_new(NAcdProbeConfig **configp);
NAcdProbeConfig *n_acd_probe_config_free(NAcdProbeConfig *config);
void n_acd_probe_config_set_ip(NAcdProbeConfig *config, struct in_addr ip);
void n_acd_probe_config_set_timeout(NAcdProbeConfig *config, uint64_t msecs);
/* contexts */
int n_acd_new(NAcd **acdp, NAcdConfig *config);
NAcd *n_acd_ref(NAcd *acd);
NAcd *n_acd_unref(NAcd *acd);
void n_acd_get_fd(NAcd *acd, int *fdp);
int n_acd_dispatch(NAcd *acd);
int n_acd_pop_event(NAcd *acd, NAcdEvent **eventp);
int n_acd_announce(NAcd *acd, unsigned int defend);
int n_acd_start(NAcd *acd, NAcdConfig *config);
int n_acd_stop(NAcd *acd);
int n_acd_probe(NAcd *acd, NAcdProbe **probep, NAcdProbeConfig *config);
/* probes */
NAcdProbe *n_acd_probe_free(NAcdProbe *probe);
void n_acd_probe_set_userdata(NAcdProbe *probe, void *userdata);
void n_acd_probe_get_userdata(NAcdProbe *probe, void **userdatap);
static inline void n_acd_freep(NAcd **acd) {
int n_acd_probe_announce(NAcdProbe *probe, unsigned int defend);
/* inline helpers */
static inline void n_acd_config_freep(NAcdConfig **config) {
if (*config)
n_acd_config_free(*config);
}
static inline void n_acd_config_freev(NAcdConfig *config) {
n_acd_config_free(config);
}
static inline void n_acd_probe_config_freep(NAcdProbeConfig **config) {
if (*config)
n_acd_probe_config_free(*config);
}
static inline void n_acd_probe_config_freev(NAcdProbeConfig *config) {
n_acd_probe_config_free(config);
}
static inline void n_acd_unrefp(NAcd **acd) {
if (*acd)
n_acd_free(*acd);
n_acd_unref(*acd);
}
static inline void n_acd_unrefv(