Commit 23cbce4b authored by Beniamino Galvani's avatar Beniamino Galvani

Squashed 'shared/n-acd/' content from commit a68b55992

git-subtree-dir: shared/n-acd
git-subtree-split: a68b55992dd7b38bdb9dbbdba4a9284ff2c2cce3
parents
# 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
[submodule "subprojects/c-list"]
path = subprojects/c-list
url = https://github.com/c-util/c-list.git
[submodule "subprojects/c-siphash"]
path = subprojects/c-siphash
url = https://github.com/c-util/c-siphash.git
dist: trusty
sudo: required
os: linux
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
script:
- meson "build"
- ninja -C "build"
- sudo MESON_TESTTHREADS=64 ninja -C "build" test
LICENSE:
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
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
COPYRIGHT: (ordered alphabetically)
Copyright (C) 2015-2017 Red Hat, Inc.
AUTHORS: (ordered alphabetically)
David Herrmann <dh.herrmann@gmail.com>
Tom Gundersen <teg@jklm.no>
This diff is collapsed.
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://github.com/nettools/n-acd/wiki
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
LICENSE:
Apache Software License 2.0 (LICENSE)
See COPYING for details.
REQUIREMENTS:
The requirements for n-acd are:
Linux kernel >= 3.0
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 n-acd. Contact upstream
documentation for detailed help. In most situations the following
commands are sufficient to build and install n-acd from source:
$ mkdir build
$ cd build
$ meson setup . ..
$ ninja
$ ninja test
# ninja install
No custom configuration options are available.
project('n-acd',
'c',
version: '1',
license: 'Apache',
default_options: [
'buildtype=release',
'c_std=c11',
])
add_project_arguments('-D_GNU_SOURCE', language: 'c')
mod_pkgconfig = import('pkgconfig')
sub_clist = subproject('c-list')
sub_csiphash = subproject('c-siphash')
dep_clist = sub_clist.get_variable('libclist_dep')
dep_csiphash = sub_csiphash.get_variable('libcsiphash_dep')
subdir('src')
LIBNACD_1 {
global:
n_acd_new;
n_acd_free;
n_acd_get_fd;
n_acd_dispatch;
n_acd_pop_event;
n_acd_start;
n_acd_stop;
n_acd_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.
#
test_api = executable('test-api',
['test-api.c'],
link_with: libnacd_shared)
test('API Symbol Visibility', test_api)
#
# 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_loopback = executable('test-loopback',
['test-loopback.c'],
link_with: libnacd_private)
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_unplug = executable('test-unplug',
['test-unplug.c'],
link_with: libnacd_private)
test('Async Interface Hotplug', test_unplug)
test_unused = executable('test-unsed',
['test-unused.c'],
link_with: libnacd_private)
test('Unconflicted ACD', test_unused)
This diff is collapsed.
#pragma once
/*
* IPv4 Address Conflict Detection
*
* This is the public header of the n-acd library, implementing IPv4 Address
* Conflict Detection as described in RFC-5227. This header defines the public
* API and all entry points of n-acd.
*/
#ifdef __cplusplus
extern "C" {
#endif
#include <netinet/in.h>
#include <stdbool.h>
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;
enum {
N_ACD_TRANSPORT_ETHERNET,
_N_ACD_TRANSPORT_N,
};
enum {
N_ACD_EVENT_READY,
N_ACD_EVENT_USED,
N_ACD_EVENT_DEFENDED,
N_ACD_EVENT_CONFLICT,
N_ACD_EVENT_DOWN,
_N_ACD_EVENT_N,
};
enum {
N_ACD_DEFEND_NEVER,
N_ACD_DEFEND_ONCE,
N_ACD_DEFEND_ALWAYS,
_N_ACD_DEFEND_N,
};
int n_acd_new(NAcd **acdp);
NAcd *n_acd_free(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);
static inline void n_acd_freep(NAcd **acd) {
if (*acd)
n_acd_free(*acd);
}
#ifdef __cplusplus
}
#endif
/*
* Tests for n-acd API
* This verifies the visibility and availability of the public API of the
* n-acd library.
*/
#include <stdlib.h>
#include "test.h"
static void test_api_constants(void) {
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);
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);
}
static void test_api_management(void) {
NAcd *acd = NULL;
int r;
/* new/free/freep */
n_acd_freep(&acd);
r = n_acd_new(&acd);
assert(!r);
n_acd_free(acd);
}
static void test_api_runtime(void) {
NAcdConfig config = {
.ifindex = 1,
.transport = N_ACD_TRANSPORT_ETHERNET,
.mac = (uint8_t[]){ 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54 },
.n_mac = ETH_ALEN,
.ip = { htobe32((127 << 24) | (1 << 0)) },
.timeout_msec = 100,
};
NAcd *acd;
int r;
/* get_fd/dispatch/pop_event/start/stop/announce */
r = n_acd_new(&acd);
assert(!r);
n_acd_get_fd(acd, &r);
assert(r >= 0);
r = n_acd_dispatch(acd);
assert(!r);
r = n_acd_pop_event(acd, NULL);
assert(r == N_ACD_E_STOPPED);
r = n_acd_start(acd, &config);
assert(!r);
r = n_acd_start(acd, &config);
assert(r == N_ACD_E_BUSY);
r = n_acd_pop_event(acd, NULL);
assert(r == N_ACD_E_DONE);
r = n_acd_stop(acd);
assert(!r);
r = n_acd_announce(acd, N_ACD_DEFEND_NEVER);
assert(r == N_ACD_E_BUSY);
n_acd_free(acd);
}
int main(int argc, char **argv) {
int r;
r = test_setup();
if (r)
return r;
test_api_constants();
test_api_management();
test_api_runtime();
return 0;
}
/*
* Basic Tests
*/
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "n-acd.h"
int main(int argc, char **argv) {
return 0;
}
/*
* Test on loopback device
* This runs the ACD engine on the loopback device, effectively testing the BPF
* filter of ACD to discard its own packets. This might happen on
* non-spanning-tree networks, or on networks that echo packets.
*/
#include <stdlib.h>
#include "test.h"
static void test_loopback(int ifindex, uint8_t *mac, size_t n_mac) {
NAcdConfig config = {
.ifindex = ifindex,
.transport = N_ACD_TRANSPORT_ETHERNET,
.mac = mac,
.n_mac = n_mac,
.ip = { htobe32((192 << 24) | (168 << 16) | (1 << 0)) },
.timeout_msec = 100,
};
struct pollfd pfds;
NAcd *acd;
int r, fd;
r = n_acd_new(&acd);
assert(!r);
n_acd_get_fd(acd, &fd);
r = n_acd_start(acd, &config);
assert(!r);
for (;;) {
NAcdEvent *event;
pfds = (struct pollfd){ .fd = fd, .events = POLLIN };
r = poll(&pfds, 1, -1);
assert(r >= 0);
r = n_acd_dispatch(acd);
assert(!r);
r = n_acd_pop_event(acd, &event);
if (!r) {
assert(event->event == N_ACD_EVENT_READY);
break;
} else {
assert(r == N_ACD_E_DONE);
}
}
n_acd_free(acd);
}
int main(int argc, char **argv) {
struct ether_addr mac;
int r, ifindex;
r = test_setup();
if (r)
return r;
r = system("ip link set lo up");
assert(r == 0);
test_if_query("lo", &ifindex, &mac);
test_loopback(ifindex, mac.ether_addr_octet, sizeof(mac.ether_addr_octet));
return 0;
}
/*
* Test with unused address twice in parallel
* This runs the ACD engine with an unused address on a veth pair, but it runs
* it on both ends. We expect the PROBE to fail on at least one of the devices.
*/
#include <stdlib.h>
#include "test.h"
static void test_unused(int ifindex1, uint8_t *mac1, size_t n_mac1, int ifindex2, uint8_t *mac2, size_t n_mac2) {
NAcdConfig config1 = {
.ifindex = ifindex1,
.transport = N_ACD_TRANSPORT_ETHERNET,
.mac = mac1,
.n_mac = n_mac1,
.ip = { htobe32((192 << 24) | (168 << 16) | (1 << 0)) },
.timeout_msec = 100,
};
NAcdConfig config2 = {
.ifindex = ifindex2,
.transport = N_ACD_TRANSPORT_ETHERNET,
.mac = mac2,
.n_mac = n_mac2,
.ip = { htobe32((192 << 24) | (168 << 16) | (1 << 0)) },
.timeout_msec = 100,
};
struct pollfd pfds[2];
NAcd *acd1, *acd2;
int r, fd1, fd2, state1, state2;
r = n_acd_new(&acd1);
assert(!r);
r = n_acd_new(&acd2);
assert(!r);
n_acd_get_fd(acd1, &fd1);
n_acd_get_fd(acd2, &fd2);
r = n_acd_start(acd1, &config1);
assert(!r);
r = n_acd_start(acd2, &config2);
assert(!r);
for (state1 = state2 = -1; state1 == -1 || state2 == -1; ) {
NAcdEvent *event;
pfds[0] = (struct pollfd){ .fd = fd1, .events = (state1 == -1) ? POLLIN : 0 };
pfds[1] = (struct pollfd){ .fd = fd2, .events = (state2 == -1) ? POLLIN : 0 };
r = poll(pfds, sizeof(pfds) / sizeof(*pfds), -1);
assert(r >= 0);
if (state1 == -1) {
r = n_acd_dispatch(acd1);
assert(!r);
r = n_acd_pop_event(acd1, &event);
if (!r) {
assert(event->event == N_ACD_EVENT_READY || event->event == N_ACD_EVENT_USED);
state1 = !!(event->event == N_ACD_EVENT_READY);
} else {
assert(r == N_ACD_E_DONE);
}
}
if (state2 == -1) {
r = n_acd_dispatch(acd2);
assert(!r);
r = n_acd_pop_event(acd2, &event);
if (!r) {
assert(event->event == N_ACD_EVENT_READY || event->event == N_ACD_EVENT_USED);
state2 = !!(event->event == N_ACD_EVENT_READY);
} else {
assert(r == N_ACD_E_DONE);
}
}
}
n_acd_free(acd1);
n_acd_free(acd2);
assert(!state1 || !state2);
}
int main(int argc, char **argv) {
struct ether_addr mac1, mac2;
int r, ifindex1, ifindex2;
r = test_setup();
if (r)
return r;
test_veth_new(&ifindex1, &mac1, &ifindex2, &mac2);
test_unused(ifindex1, mac1.ether_addr_octet, sizeof(mac2.ether_addr_octet), ifindex2, mac2.ether_addr_octet, sizeof(mac2.ether_addr_octet));
return 0;
}
/*
* Unplug device during test run
* Run the ACD engine with an address that is not used by anyone else on the
* link, but DOWN or UNPLUG the device while running.
*/
#include <stdlib.h>
#include "test.h"
static void test_unplug_down(int ifindex, uint8_t *mac, size_t n_mac, unsigned int run) {
NAcdConfig config = {
.ifindex = ifindex,
.transport = N_ACD_TRANSPORT_ETHERNET,
.mac = mac,
.n_mac = n_mac,
.ip = { htobe32((192 << 24) | (168 << 16) | (1 << 0)) },
.timeout_msec = 100,
};
struct pollfd pfds;
NAcd *acd;
int r, fd;
if (!run--)
test_veth_cmd(ifindex, "down");
r = n_acd_new(&acd);
assert(!r);
if (!run--)
test_veth_cmd(ifindex, "down");
n_acd_get_fd(acd, &fd);
r = n_acd_start(acd, &config);
assert(!r);
if (!run--)
test_veth_cmd(ifindex, "down");
for (;;) {
NAcdEvent *event;
pfds = (struct pollfd){ .fd = fd, .events = POLLIN };
r = poll(&pfds, 1, -1);
assert(r >= 0);
if (!run--)
test_veth_cmd(ifindex, "down");
r = n_acd_dispatch(acd);
assert(!r);
r = n_acd_pop_event(acd, &event);
if (!r) {
if (event->event == N_ACD_EVENT_DOWN) {
break;
} else {
assert(event->event == N_ACD_EVENT_READY);
test_veth_cmd(ifindex, "down");
}
} else {
assert(r == N_ACD_E_DONE);
}
}
n_acd_free(acd);
}
int main(int argc, char **argv) {
struct ether_addr mac;
unsigned int i;
int r, ifindex;
r = test_setup();
if (r)
return r;
test_veth_new(&ifindex, &mac, NULL, NULL);
for (i = 0; i < 5; ++i) {
test_unplug_down(ifindex, mac.ether_addr_octet, sizeof(mac.ether_addr_octet), i);
test_veth_cmd(ifindex, "up");
}
return 0;
}
/*
* Test with unused address
* Run the ACD engine with an address that is not used by anyone else on the
* link. This should just pass through, with a short, random timeout.
*/
#include <stdlib.h>
#include "test.h"
static void test_unused(int ifindex, const uint8_t *mac, size_t n_mac) {
NAcdConfig config = {
.ifindex = ifindex,
.transport = N_ACD_TRANSPORT_ETHERNET,
.mac = mac,
.n_mac = n_mac,
.ip = { htobe32((192 << 24) | (168 << 16) | (1 << 0)) },
.timeout_msec = 100,
};
struct pollfd pfds;
NAcd *acd;
int r, fd;
r = n_acd_new(&acd);
assert(!r);
n_acd_get_fd(acd, &fd);
r = n_acd_start(acd, &config);
assert(!r);
for (;;) {
NAcdEvent *event;
pfds = (struct pollfd){ .fd = fd, .events = POLLIN };
r = poll(&pfds, 1, -1);
assert(r >= 0);
r = n_acd_dispatch(acd);
assert(!r);
r = n_acd_pop_event(acd, &event);
if (!r) {
assert(event->event == N_ACD_EVENT_READY);
break;
} else {
assert(r == N_ACD_E_DONE);
}
}
n_acd_free(acd);
}
int main(int argc, char **argv) {
struct ether_addr mac;
int r, ifindex;
r = test_setup();
if (r)
return r;
test_veth_new(&ifindex, &mac, NULL, NULL);
test_unused(ifindex, mac.ether_addr_octet, sizeof(mac.ether_addr_octet));
return 0;
}
#pragma once
/*
* Test Helpers
* Bunch of helpers to setup the environment for networking tests. This
* includes net-namespace setups, veth setups, and more.
*/
#include <assert.h>
#include <endian.h>
#include <errno.h>
#include <net/ethernet.h>
#include <net/if.h>
#include <netinet/in.h>
#include <poll.h>
#include <sched.h>
#include <stdbool.h>
#include <stdio.h>