Commit 90a0edce authored by Thomas Haller's avatar Thomas Haller

Squashed 'shared/n-acd/' changes from 9eb7bf7173..5470816839

5470816839 test: increase timeout on test-veth
d44dfa1ba7 build: update c-stdaux
26b10b6514 build: pull in submodule updates
7817fc0a95 n-acd: switch to c-stdaux
5033b2ecdd n-acd: include dependency headers
aaf2a66788 build: update README
b9448eff98 build: pull in c-stdaux
8ac364e9a3 test: raise MEMLOCK if possible
3cd197162e ci: drop root
0289a33412 test: allow running without root
67a343fe87 build: update email address
3c364ba95f build: bump version
d0f7d71fa1 build: document build configuration options
014b00cd27 build: fill in NEWS
180990288a n-acd: document API
79904585df build: update submodules
8185e6ed89 build: reduce boilerplate

git-subtree-dir: shared/n-acd
git-subtree-split: 54708168399f1662c652b5931608e5077ef462f6
parent 7e776266
......@@ -8,5 +8,5 @@ cd "./ci-build"
${CHERRY_LIB_MESONSETUP} . "${CHERRY_LIB_SRCDIR}" ${N_ACD_CONF}
${CHERRY_LIB_NINJABUILD}
sudo ${CHERRY_LIB_MESONTEST}
${CHERRY_LIB_MESONTEST}
# no valgrind tests, since bpf(2) is not supported by it
......@@ -7,3 +7,6 @@
[submodule "subprojects/c-rbtree"]
path = subprojects/c-rbtree
url = https://github.com/c-util/c-rbtree.git
[submodule "subprojects/c-stdaux"]
path = subprojects/c-stdaux
url = https://github.com/c-util/c-stdaux.git
......@@ -30,10 +30,10 @@ AUTHORS-LGPL:
along with this program; If not, see <http://www.gnu.org/licenses/>.
COPYRIGHT: (ordered alphabetically)
Copyright (C) 2015-2018 Red Hat, Inc.
Copyright (C) 2015-2019 Red Hat, Inc.
AUTHORS: (ordered alphabetically)
Beniamino Galvani <bgalvani@redhat.com>
David Herrmann <dh.herrmann@gmail.com>
David Rheinsberg <david.rheinsberg@gmail.com>
Thomas Haller <thaller@redhat.com>
Tom Gundersen <teg@jklm.no>
This diff is collapsed.
This diff is collapsed.
AUTHORS-ASL
\ No newline at end of file
n-acd - IPv4 Address Conflict Detection
# n-acd - IPv4 Address Conflict Detection
CHANGES WITH 1:
## CHANGES WITH 2:
* All public destructors now include a variant that returns `void`.
This was requested for easier integration with `glib` and friends.
Similar to the `cleanup` variants, these variants are denoted by a
single-character function-name suffix. E.g., `n_acd_freev()`
* A fallback to `CLOCK_MONOTONIC` is now provided in case
`CLOCK_BOOTTIME` is not supported by the kernel. Note that this is in
no way signalled through the API, so if timers should follow the
`BOOTTIME` rather than monotonic clock, a kernel with this clock is
required.
* The `c-sundry` dependency is no longer needed.
* The `transport` configuration property is now mandatory for
`n_acd_new()`. It defaulted to `ETHERNET` before, by mistake.
* In-source documentation for the public API is now provided.
Contributions from: Beniamino Galvani, David Herrmann, David
Rheinsberg, Thomas Haller, Tom Gundersen
- Tübingen, 2019-03-20
## 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
......
n-acd - IPv4 Address Conflict Detection
ABOUT:
The n-acd 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.
DETAILS:
https://nettools.github.io/n-acd
BUG REPORTS:
https://github.com/nettools/n-acd/issues
GIT:
git@github.com:nettools/n-acd.git
https://github.com/nettools/n-acd.git
GITWEB:
https://github.com/nettools/n-acd
MAILINGLIST:
https://groups.google.com/forum/#!forum/nettools-devel
LICENSE:
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.19
libc (e.g., glibc >= 2.16)
At build-time, the following software is required:
meson >= 0.41
pkg-config >= 0.29
INSTALL:
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 from source:
$ mkdir build
$ cd build
$ meson setup ..
$ ninja
$ meson test
# ninja install
For custom configuration options see meson_options.txt.
n-acd
=====
IPv4 Address Conflict Detection
The n-acd 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.
### Project
* **Website**: <https://nettools.github.io/n-acd>
* **Bug Tracker**: <https://github.com/nettools/n-acd/issues>
* **Mailing-List**: <https://groups.google.com/forum/#!forum/nettools-devel>
### Requirements
The requirements for this project are:
* `Linux kernel >= 3.19`
* `libc` (e.g., `glibc >= 2.16`)
At build-time, the following software is required:
* `meson >= 0.41`
* `pkg-config >= 0.29`
### Build
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 from source:
```sh
mkdir build
cd build
meson setup ..
ninja
meson test
ninja install
```
The following configuration options are available:
* `ebpf`: This boolean controls whether `ebpf` features are used to improve
the package filtering performance. If disabled, classic bpf will be
used. This feature requires a rather recent kernel (>=3.19).
Default is: true
### Repository:
- **web**: <https://github.com/nettools/n-acd>
- **https**: `https://github.com/nettools/n-acd.git`
- **ssh**: `git@github.com:nettools/n-acd.git`
### License:
- **Apache-2.0** OR **LGPL-2.1-or-later**
- See AUTHORS file for details.
project(
'n-acd',
'c',
version: '1',
version: '2',
license: 'Apache',
default_options: [
'c_std=c11',
......@@ -15,10 +15,12 @@ mod_pkgconfig = import('pkgconfig')
sub_clist = subproject('c-list')
sub_crbtree = subproject('c-rbtree')
sub_csiphash = subproject('c-siphash')
sub_cstdaux = subproject('c-stdaux')
dep_clist = sub_clist.get_variable('libclist_dep')
dep_crbtree = sub_crbtree.get_variable('libcrbtree_dep')
dep_csiphash = sub_csiphash.get_variable('libcsiphash_dep')
dep_cstdaux = sub_cstdaux.get_variable('libcstdaux_dep')
use_ebpf = get_option('ebpf')
......
......@@ -8,6 +8,7 @@ libnacd_deps = [
dep_clist,
dep_crbtree,
dep_csiphash,
dep_cstdaux,
]
libnacd_sources = [
......
......@@ -7,6 +7,7 @@
* See n-acd-bpf.c for documentation.
*/
#include <c-stdaux.h>
#include <stddef.h>
#include "n-acd-private.h"
......
......@@ -12,6 +12,7 @@
* that have already been removed.
*/
#include <c-stdaux.h>
#include <errno.h>
#include <inttypes.h>
#include <linux/bpf.h>
......
......@@ -2,6 +2,7 @@
#include <c-list.h>
#include <c-rbtree.h>
#include <c-stdaux.h>
#include <errno.h>
#include <inttypes.h>
#include <netinet/if_ether.h>
......@@ -13,9 +14,6 @@
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,
......@@ -150,23 +148,7 @@ 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);
}
/*
* IPv4 Address Conflict Detection
*
* This file implements the probe object. A probe is basically the
* state-machine of a single ACD run. It takes an address to probe for, checks
* for conflicts and then defends it once configured.
*/
#include <assert.h>
#include <c-rbtree.h>
#include <c-stdaux.h>
#include <endian.h>
#include <errno.h>
#include <inttypes.h>
......@@ -19,8 +24,8 @@
#include "n-acd-private.h"
/*
* These parameters and timing intervals specified in RFC-5227. The original
* values are:
* These parameters and timing intervals are specified in RFC-5227. The
* original values are:
*
* PROBE_NUM 3
* PROBE_WAIT 1s
......@@ -66,10 +71,19 @@
#define N_ACD_RFC_DEFEND_INTERVAL_NSEC (UINT64_C(10000000000)) /* 10s */
/**
* XXX
* n_acd_probe_config_new() - create probe configuration
* @configp: output argument for new probe configuration
*
* This creates a new probe configuration. It will be returned in @configp to
* the caller, which upon return fully owns the object.
*
* A probe configuration collects parameters for probes. It never validates the
* input, but this is left to the consumer of the configuration to do.
*
* Return: 0 on success, negative error code on failure.
*/
_public_ int n_acd_probe_config_new(NAcdProbeConfig **configp) {
_cleanup_(n_acd_probe_config_freep) NAcdProbeConfig *config = NULL;
_c_public_ int n_acd_probe_config_new(NAcdProbeConfig **configp) {
_c_cleanup_(n_acd_probe_config_freep) NAcdProbeConfig *config = NULL;
config = malloc(sizeof(*config));
if (!config)
......@@ -83,9 +97,15 @@ _public_ int n_acd_probe_config_new(NAcdProbeConfig **configp) {
}
/**
* XXX
* n_acd_probe_config_free() - destroy probe configuration
* @config: configuration to operate on, or NULL
*
* This destroys the probe configuration and all associated objects. If @config
* is NULL, this is a no-op.
*
* Return: NULL is returned.
*/
_public_ NAcdProbeConfig *n_acd_probe_config_free(NAcdProbeConfig *config) {
_c_public_ NAcdProbeConfig *n_acd_probe_config_free(NAcdProbeConfig *config) {
if (!config)
return NULL;
......@@ -95,16 +115,45 @@ _public_ NAcdProbeConfig *n_acd_probe_config_free(NAcdProbeConfig *config) {
}
/**
* XXX
* n_acd_probe_config_set_ip() - set ip property
* @config: configuration to operate on
* @ip: ip to set
*
* This sets the IP property to the value `ip`. The address is copied into the
* configuration object. No validation is performed.
*
* The IP property selects the IP address that a probe checks for. It is the
* caller's responsibility to guarantee the address is valid and can be used.
*/
_public_ void n_acd_probe_config_set_ip(NAcdProbeConfig *config, struct in_addr ip) {
_c_public_ void n_acd_probe_config_set_ip(NAcdProbeConfig *config, struct in_addr ip) {
config->ip = ip;
}
/**
* XXX
* n_acd_probe_config_set_timeout() - set timeout property
* @config: configuration to operate on
* @msecs: timeout to set, in milliseconds
*
* This sets the timeout to use for a conflict detection probe. The
* specification default is provided as `N_ACD_TIMEOUT_RFC5227` and corresponds
* to 9 seconds.
*
* If set to 0, conflict detection is skipped and the address is immediately
* advertised and defended.
*
* Depending on the transport used, the API user should select a suitable
* timeout. Since `ACD` only operates on the link layer, timeouts in the
* hundreds of milliseconds range should be more than enough for any modern
* network. Note that increasing this value directly affects the time it takes
* to connect to a network, since an address should not be used unless conflict
* detection finishes.
*
* Using the specification default is **discouraged**. It is way too slow and
* not appropriate for modern networks.
*
* Default value is `N_ACD_TIMEOUT_RFC5227`.
*/
_public_ void n_acd_probe_config_set_timeout(NAcdProbeConfig *config, uint64_t msecs) {
_c_public_ void n_acd_probe_config_set_timeout(NAcdProbeConfig *config, uint64_t msecs) {
config->timeout_msecs = msecs;
}
......@@ -214,14 +263,14 @@ static void n_acd_probe_unlink(NAcdProbe *probe) {
*/
if (n_acd_probe_is_unique(probe)) {
r = n_acd_bpf_map_remove(probe->acd->fd_bpf_map, &probe->ip);
assert(r >= 0);
c_assert(r >= 0);
--probe->acd->n_bpf_map;
}
c_rbnode_unlink(&probe->ip_node);
}
int n_acd_probe_new(NAcdProbe **probep, NAcd *acd, NAcdProbeConfig *config) {
_cleanup_(n_acd_probe_freep) NAcdProbe *probe = NULL;
_c_cleanup_(n_acd_probe_freep) NAcdProbe *probe = NULL;
int r;
if (!config->ip.s_addr)
......@@ -285,9 +334,20 @@ int n_acd_probe_new(NAcdProbe **probep, NAcd *acd, NAcdProbeConfig *config) {
}
/**
* XXX
* n_acd_probe_free() - destroy a probe
* @probe: probe to operate on, or NULL
*
* This destroys the probe specified by @probe. All operations are immediately
* ceded and all associated objects are released.
*
* If @probe is NULL, this is a no-op.
*
* This function will flush all events associated with @probe from the event
* queue. That is, no events will be returned for this @probe anymore.
*
* Return: NULL is returned.
*/
_public_ NAcdProbe *n_acd_probe_free(NAcdProbe *probe) {
_c_public_ NAcdProbe *n_acd_probe_free(NAcdProbe *probe) {
NAcdEventNode *node, *t_node;
if (!probe)
......@@ -305,7 +365,7 @@ _public_ NAcdProbe *n_acd_probe_free(NAcdProbe *probe) {
}
int n_acd_probe_raise(NAcdProbe *probe, NAcdEventNode **nodep, unsigned int event) {
_cleanup_(n_acd_event_node_freep) NAcdEventNode *node = NULL;
_c_cleanup_(n_acd_event_node_freep) NAcdEventNode *node = NULL;
int r;
r = n_acd_raise(probe->acd, &node, event);
......@@ -326,8 +386,8 @@ int n_acd_probe_raise(NAcdProbe *probe, NAcdEventNode **nodep, unsigned int even
node->event.conflict.probe = probe;
break;
default:
assert(0);
return -EIO;
c_assert(0);
return -ENOTRECOVERABLE;
}
c_list_link_tail(&probe->event_list, &node->probe_link);
......@@ -450,8 +510,8 @@ int n_acd_probe_handle_timeout(NAcdProbe *probe) {
* There are no timeouts in these states. If we trigger one,
* something is fishy.
*/
assert(0);
return -EIO;
c_assert(0);
return -ENOTRECOVERABLE;
}
return 0;
......@@ -582,30 +642,46 @@ int n_acd_probe_handle_packet(NAcdProbe *probe, struct ether_arp *packet, bool h
* We are not listening for packets in these states. If we receive one,
* something is fishy.
*/
assert(0);
return -EIO;
c_assert(0);
return -ENOTRECOVERABLE;
}
return 0;
}
/**
* n_acd_probe_set_userdata - XXX
* n_acd_probe_set_userdata - set userdata
* @probe: probe to operate on
* @userdata: userdata pointer
*
* This can be used to set a caller-controlled user-data pointer on @probe. The
* value of the pointer is never inspected or used by `n-acd` and is fully
* under control of the caller.
*
* The default value is NULL.
*/
_public_ void n_acd_probe_set_userdata(NAcdProbe *probe, void *userdata) {
_c_public_ void n_acd_probe_set_userdata(NAcdProbe *probe, void *userdata) {
probe->userdata = userdata;
}
/**
* n_acd_probe_get_userdata - XXX
* n_acd_probe_get_userdata - get userdata
* @probe: probe to operate on
*
* This queries the userdata pointer that was previously set through
* n_acd_probe_set_userdata().
*
* The default value is NULL.
*
* Return: The stored userdata pointer is returned.
*/
_public_ void n_acd_probe_get_userdata(NAcdProbe *probe, void **userdatap) {
_c_public_ void n_acd_probe_get_userdata(NAcdProbe *probe, void **userdatap) {
*userdatap = probe->userdata;
}
/**
* n_acd_probe_announce() - announce the configured IP address
* @probe: probe object
* @probe: probe to operate on
* @defend: defence policy
*
* Announce the IP address on the local link, and start defending it according
......@@ -618,7 +694,7 @@ _public_ void n_acd_probe_get_userdata(NAcdProbe *probe, void **userdatap) {
* Return: 0 on success, N_ACD_E_INVALID_ARGUMENT in case the defence policy
* is invalid, negative error code on failure.
*/
_public_ int n_acd_probe_announce(NAcdProbe *probe, unsigned int defend) {
_c_public_ int n_acd_probe_announce(NAcdProbe *probe, unsigned int defend) {
if (defend >= _N_ACD_DEFEND_N)
return N_ACD_E_INVALID_ARGUMENT;
......
This diff is collapsed.
......@@ -13,7 +13,9 @@ extern "C" {
#endif
#include <netinet/in.h>
#include <inttypes.h>
#include <stdbool.h>
#include <stdlib.h>
typedef struct NAcd NAcd;
typedef struct NAcdConfig NAcdConfig;
......
/*
* Tests for n-acd API
* This verifies the visibility and availability of the public API of the
* n-acd library.
* This verifies the visibility and availability of the public API.
*/
#undef NDEBUG
#include <assert.h>
#include <stdlib.h>
#include "test.h"
static void test_api(void) {
NAcdConfig *config = NULL;
NAcd *acd = NULL;
int r;
assert(N_ACD_E_PREEMPTED);
assert(N_ACD_E_INVALID_ARGUMENT);
assert(N_ACD_TRANSPORT_ETHERNET != _N_ACD_TRANSPORT_N);
assert(N_ACD_EVENT_READY != _N_ACD_EVENT_N);
assert(N_ACD_EVENT_USED != _N_ACD_EVENT_N);
assert(N_ACD_EVENT_DEFENDED != _N_ACD_EVENT_N);
assert(N_ACD_EVENT_CONFLICT != _N_ACD_EVENT_N);
assert(N_ACD_EVENT_DOWN != _N_ACD_EVENT_N);
assert(N_ACD_DEFEND_NEVER != _N_ACD_DEFEND_N);
assert(N_ACD_DEFEND_ONCE != _N_ACD_DEFEND_N);
assert(N_ACD_DEFEND_ALWAYS != _N_ACD_DEFEND_N);
n_acd_config_freep(&config);
r = n_acd_config_new(&config);
assert(!r);
n_acd_config_set_ifindex(config, 1);
n_acd_config_set_transport(config, N_ACD_TRANSPORT_ETHERNET);
n_acd_config_set_mac(config, (uint8_t[6]){ }, 6);
{
NAcdEvent *event;
int fd;
n_acd_unrefp(&acd);
n_acd_ref(NULL);
r = n_acd_new(&acd, config);
assert(!r);
n_acd_get_fd(acd, &fd);
n_acd_dispatch(acd);
n_acd_pop_event(acd, &event);
{
NAcdProbeConfig *c = NULL;
n_acd_probe_config_freep(&c);
r = n_acd_probe_config_new(&c);
assert(!r);
n_acd_probe_config_set_ip(c, (struct in_addr){ 1 });
n_acd_probe_config_set_timeout(c, N_ACD_TIMEOUT_RFC5227);
{
NAcdProbe *probe = NULL;
void *userdata;
r = n_acd_probe(acd, &probe, c);
assert(!r);
n_acd_probe_get_userdata(probe, &userdata);
assert(userdata == NULL);
n_acd_probe_set_userdata(probe, acd);
n_acd_probe_get_userdata(probe, &userdata);
assert(userdata == acd);
r = n_acd_probe_announce(probe, N_ACD_DEFEND_ONCE);
assert(!r);
n_acd_probe_free(probe);
n_acd_probe_freev(NULL);
}
n_acd_probe_config_free(c);
n_acd_probe_config_freev(NULL);
}
#include "n-acd.h"
static void test_api_constants(void) {
assert(1 + N_ACD_TIMEOUT_RFC5227);
assert(1 + _N_ACD_E_SUCCESS);
assert(1 + N_ACD_E_PREEMPTED);
assert(1 + N_ACD_E_INVALID_ARGUMENT);
assert(1 + _N_ACD_E_N);
assert(1 + N_ACD_TRANSPORT_ETHERNET);
assert(1 + _N_ACD_TRANSPORT_N);
assert(1 + N_ACD_EVENT_READY);
assert(1 + N_ACD_EVENT_USED);
assert(1 + N_ACD_EVENT_DEFENDED);
assert(1 + N_ACD_EVENT_CONFLICT);
assert(1 + N_ACD_EVENT_DOWN);
assert(1 + _N_ACD_EVENT_N);
assert(1 + N_ACD_DEFEND_NEVER);
assert(1 + N_ACD_DEFEND_ONCE);
assert(1 + N_ACD_DEFEND_ALWAYS);
assert(1 + _N_ACD_DEFEND_N);
}
n_acd_unref(acd);
n_acd_unrefv(NULL);
}
static void test_api_types(void) {
assert(sizeof(NAcdEvent*));
assert(sizeof(NAcdConfig*));
assert(sizeof(NAcdProbeConfig*));
assert(sizeof(NAcd*));
assert(sizeof(NAcdProbe*));
}
n_acd_config_free(config);
n_acd_config_freev(NULL);
static void test_api_functions(void) {
void *fns[] = {
(void *)n_acd_config_new,
(void *)n_acd_config_free,
(void *)n_acd_config_set_ifindex,
(void *)n_acd_config_set_transport,
(void *)n_acd_config_set_mac,
(void *)n_acd_probe_config_new,
(void *)n_acd_probe_config_free,
(void *)n_acd_probe_config_set_ip,
(void *)n_acd_probe_config_set_timeout,
(void *)n_acd_new,
(void *)n_acd_ref,
(void *)n_acd_unref,
(void *)n_acd_get_fd,
(void *)n_acd_dispatch,
(void *)n_acd_pop_event,
(void *)n_acd_probe,
(void *)n_acd_probe_free,
(void *)n_acd_probe_set_userdata,
(void *)n_acd_probe_get_userdata,
(void *)n_acd_probe_announce,
(void *)n_acd_config_freep,
(void *)n_acd_config_freev,
(void *)n_acd_probe_config_freep,
(void *)n_acd_probe_config_freev,
(void *)n_acd_unrefp,
(void *)n_acd_unrefv,
(void *)n_acd_probe_freep,
(void *)n_acd_probe_freev,
};
size_t i;
for (i = 0; i < sizeof(fns) / sizeof(*fns); ++i)
assert(!!fns[i]);
}
int main(int argc, char **argv) {
int r;
r = test_setup();
if (r)
return r;
test_api();
test_api_constants();
test_api_types();