summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/cgtop/cgtop.c57
-rw-r--r--src/core/dbus-manager.c32
-rw-r--r--src/core/kill.c5
-rw-r--r--src/core/kill.h3
-rw-r--r--src/core/load-fragment.c14
-rw-r--r--src/core/main.c21
-rw-r--r--src/core/manager.h5
-rw-r--r--src/core/shutdown.c22
-rw-r--r--src/core/unit.c26
-rw-r--r--src/libsystemd-network/arp-util.c153
-rw-r--r--src/libsystemd-network/arp-util.h (renamed from src/libsystemd-network/ipv4ll-internal.h)14
-rw-r--r--src/libsystemd-network/ipv4ll-network.c91
-rw-r--r--src/libsystemd-network/ipv4ll-packet.c71
-rw-r--r--src/libsystemd-network/sd-ipv4acd.c529
-rw-r--r--src/libsystemd-network/sd-ipv4ll.c641
-rw-r--r--src/libsystemd-network/test-acd.c117
-rw-r--r--src/libsystemd-network/test-ipv4ll-manual.c129
-rw-r--r--src/libsystemd-network/test-ipv4ll.c82
-rw-r--r--src/network/networkd-ipv4ll.c9
-rw-r--r--src/systemctl/systemctl.c38
-rw-r--r--src/systemd/sd-ipv4acd.h55
-rw-r--r--src/systemd/sd-ipv4ll.h2
22 files changed, 1385 insertions, 731 deletions
diff --git a/src/cgtop/cgtop.c b/src/cgtop/cgtop.c
index 4786a155da..1307a34ab7 100644
--- a/src/cgtop/cgtop.c
+++ b/src/cgtop/cgtop.c
@@ -36,6 +36,10 @@
#include "cgroup-util.h"
#include "build.h"
#include "fileio.h"
+#include "sd-bus.h"
+#include "bus-util.h"
+#include "bus-error.h"
+#include "unit-name.h"
typedef struct Group {
char *path;
@@ -65,6 +69,7 @@ static unsigned arg_iterations = (unsigned) -1;
static bool arg_batch = false;
static bool arg_raw = false;
static usec_t arg_delay = 1*USEC_PER_SEC;
+static char* arg_machine = NULL;
enum {
COUNT_PIDS,
@@ -645,6 +650,7 @@ static void help(void) {
" -n --iterations=N Run for N iterations before exiting\n"
" -b --batch Run in batch mode, accepting no input\n"
" --depth=DEPTH Maximum traversal depth (default: %u)\n"
+ " -M --machine= Show container\n"
, program_invocation_short_name, arg_depth);
}
@@ -669,6 +675,7 @@ static int parse_argv(int argc, char *argv[]) {
{ "cpu", optional_argument, NULL, ARG_CPU_TYPE },
{ "order", required_argument, NULL, ARG_ORDER },
{ "recursive", required_argument, NULL, ARG_RECURSIVE },
+ { "machine", required_argument, NULL, 'M' },
{}
};
@@ -678,7 +685,7 @@ static int parse_argv(int argc, char *argv[]) {
assert(argc >= 1);
assert(argv);
- while ((c = getopt_long(argc, argv, "hptcmin:brd:kP", options, NULL)) >= 0)
+ while ((c = getopt_long(argc, argv, "hptcmin:brd:kPM:", options, NULL)) >= 0)
switch (c) {
@@ -797,6 +804,10 @@ static int parse_argv(int argc, char *argv[]) {
recursive_unset = r == 0;
break;
+ case 'M':
+ arg_machine = optarg;
+ break;
+
case '?':
return -EINVAL;
@@ -826,6 +837,48 @@ static const char* counting_what(void) {
return "userspace processes (excl. kernel)";
}
+static int get_cgroup_root(char **ret) {
+ _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_bus_flush_close_unref_ sd_bus *bus = NULL;
+ _cleanup_free_ char *unit = NULL, *path = NULL;
+ const char *m;
+ int r;
+
+ if (!arg_machine) {
+ r = cg_get_root_path(ret);
+ if (r < 0)
+ return log_error_errno(r, "Failed to get root control group path: %m");
+
+ return 0;
+ }
+
+ m = strjoina("/run/systemd/machines/", arg_machine);
+ r = parse_env_file(m, NEWLINE, "SCOPE", &unit, NULL);
+ if (r < 0)
+ return log_error_errno(r, "Failed to load machine data: %m");
+
+ path = unit_dbus_path_from_name(unit);
+ if (!path)
+ return log_oom();
+
+ r = bus_open_transport(BUS_TRANSPORT_LOCAL, NULL, false, &bus);
+ if (r < 0)
+ return log_error_errno(r, "Failed to create bus connection: %m");
+
+ r = sd_bus_get_property_string(
+ bus,
+ "org.freedesktop.systemd1",
+ path,
+ unit_dbus_interface_from_name(unit),
+ "ControlGroup",
+ &error,
+ ret);
+ if (r < 0)
+ return log_error_errno(r, "Failed to query unit control group path: %s", bus_error_message(&error, r));
+
+ return 0;
+}
+
int main(int argc, char *argv[]) {
int r;
Hashmap *a = NULL, *b = NULL;
@@ -850,7 +903,7 @@ int main(int argc, char *argv[]) {
if (r <= 0)
goto finish;
- r = cg_get_root_path(&root);
+ r = get_cgroup_root(&root);
if (r < 0) {
log_error_errno(r, "Failed to get root control group path: %m");
goto finish;
diff --git a/src/core/dbus-manager.c b/src/core/dbus-manager.c
index 4e5d67fc19..561b6f8bfa 100644
--- a/src/core/dbus-manager.c
+++ b/src/core/dbus-manager.c
@@ -1201,8 +1201,10 @@ static int method_exit(sd_bus_message *message, void *userdata, sd_bus_error *er
if (r < 0)
return r;
- if (m->running_as == MANAGER_SYSTEM)
- return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Exit is only supported for user service managers.");
+ /* Exit() (in contrast to SetExitCode()) is actually allowed even if
+ * we are running on the host. It will fall back on reboot() in
+ * systemd-shutdown if it cannot do the exit() because it isn't a
+ * container. */
m->exit_code = MANAGER_EXIT;
@@ -1450,6 +1452,30 @@ static int method_unset_and_set_environment(sd_bus_message *message, void *userd
return sd_bus_reply_method_return(message, NULL);
}
+static int method_set_exit_code(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ uint8_t code;
+ Manager *m = userdata;
+ int r;
+
+ assert(message);
+ assert(m);
+
+ r = mac_selinux_access_check(message, "exit", error);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_read_basic(message, 'y', &code);
+ if (r < 0)
+ return r;
+
+ if (m->running_as == MANAGER_SYSTEM && detect_container() <= 0)
+ return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "ExitCode can only be set for user service managers or in containers.");
+
+ m->return_value = code;
+
+ return sd_bus_reply_method_return(message, NULL);
+}
+
static int method_list_unit_files(sd_bus_message *message, void *userdata, sd_bus_error *error) {
_cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
Manager *m = userdata;
@@ -1933,6 +1959,7 @@ const sd_bus_vtable bus_manager_vtable[] = {
SD_BUS_WRITABLE_PROPERTY("ShutdownWatchdogUSec", "t", bus_property_get_usec, bus_property_set_usec, offsetof(Manager, shutdown_watchdog), 0),
SD_BUS_PROPERTY("ControlGroup", "s", NULL, offsetof(Manager, cgroup_root), 0),
SD_BUS_PROPERTY("SystemState", "s", property_get_system_state, 0, 0),
+ SD_BUS_PROPERTY("ExitCode", "y", bus_property_get_unsigned, offsetof(Manager, return_value), 0),
SD_BUS_METHOD("GetUnit", "s", "o", method_get_unit, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("GetUnitByPID", "u", "o", method_get_unit_by_pid, SD_BUS_VTABLE_UNPRIVILEGED),
@@ -1986,6 +2013,7 @@ const sd_bus_vtable bus_manager_vtable[] = {
SD_BUS_METHOD("GetDefaultTarget", NULL, "s", method_get_default_target, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("PresetAllUnitFiles", "sbb", "a(sss)", method_preset_all_unit_files, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("AddDependencyUnitFiles", "asssbb", "a(sss)", method_add_dependency_unit_files, SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD("SetExitCode", "y", NULL, method_set_exit_code, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_SIGNAL("UnitNew", "so", 0),
SD_BUS_SIGNAL("UnitRemoved", "so", 0),
diff --git a/src/core/kill.c b/src/core/kill.c
index 2de71c6bf9..bddfa4460f 100644
--- a/src/core/kill.c
+++ b/src/core/kill.c
@@ -60,7 +60,10 @@ DEFINE_STRING_TABLE_LOOKUP(kill_mode, KillMode);
static const char* const kill_who_table[_KILL_WHO_MAX] = {
[KILL_MAIN] = "main",
[KILL_CONTROL] = "control",
- [KILL_ALL] = "all"
+ [KILL_ALL] = "all",
+ [KILL_MAIN_FAIL] = "main-fail",
+ [KILL_CONTROL_FAIL] = "control-fail",
+ [KILL_ALL_FAIL] = "all-fail"
};
DEFINE_STRING_TABLE_LOOKUP(kill_who, KillWho);
diff --git a/src/core/kill.h b/src/core/kill.h
index d5f125fa41..5d97abb104 100644
--- a/src/core/kill.h
+++ b/src/core/kill.h
@@ -50,6 +50,9 @@ typedef enum KillWho {
KILL_MAIN,
KILL_CONTROL,
KILL_ALL,
+ KILL_MAIN_FAIL,
+ KILL_CONTROL_FAIL,
+ KILL_ALL_FAIL,
_KILL_WHO_MAX,
_KILL_WHO_INVALID = -1
} KillWho;
diff --git a/src/core/load-fragment.c b/src/core/load-fragment.c
index 1217b4651e..fcf863c5c7 100644
--- a/src/core/load-fragment.c
+++ b/src/core/load-fragment.c
@@ -3049,6 +3049,7 @@ int config_parse_runtime_directory(
void *userdata) {
char***rt = data;
+ Unit *u = userdata;
const char *word, *state;
size_t l;
int r;
@@ -3065,12 +3066,19 @@ int config_parse_runtime_directory(
}
FOREACH_WORD_QUOTED(word, l, rvalue, state) {
- _cleanup_free_ char *n;
+ _cleanup_free_ char *t = NULL, *n = NULL;
- n = strndup(word, l);
- if (!n)
+ t = strndup(word, l);
+ if (!t)
return log_oom();
+ r = unit_name_printf(u, t, &n);
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, -r,
+ "Failed to resolve specifiers, ignoring: %s", strerror(-r));
+ continue;
+ }
+
if (!filename_is_valid(n)) {
log_syntax(unit, LOG_ERR, filename, line, EINVAL,
"Runtime directory is not valid, ignoring assignment: %s", rvalue);
diff --git a/src/core/main.c b/src/core/main.c
index 200fe740da..9b59648279 100644
--- a/src/core/main.c
+++ b/src/core/main.c
@@ -1254,6 +1254,7 @@ int main(int argc, char *argv[]) {
char *switch_root_dir = NULL, *switch_root_init = NULL;
struct rlimit saved_rlimit_nofile = RLIMIT_MAKE_CONST(0);
const char *error_message = NULL;
+ uint8_t shutdown_exit_code = 0;
#ifdef HAVE_SYSV_COMPAT
if (getpid() != 1 && strstr(program_invocation_short_name, "init")) {
@@ -1764,11 +1765,6 @@ int main(int argc, char *argv[]) {
switch (m->exit_code) {
- case MANAGER_EXIT:
- retval = EXIT_SUCCESS;
- log_debug("Exit.");
- goto finish;
-
case MANAGER_RELOAD:
log_info("Reloading.");
@@ -1810,11 +1806,13 @@ int main(int argc, char *argv[]) {
log_notice("Switching root.");
goto finish;
+ case MANAGER_EXIT:
case MANAGER_REBOOT:
case MANAGER_POWEROFF:
case MANAGER_HALT:
case MANAGER_KEXEC: {
static const char * const table[_MANAGER_EXIT_CODE_MAX] = {
+ [MANAGER_EXIT] = "exit",
[MANAGER_REBOOT] = "reboot",
[MANAGER_POWEROFF] = "poweroff",
[MANAGER_HALT] = "halt",
@@ -1836,8 +1834,10 @@ int main(int argc, char *argv[]) {
finish:
pager_close();
- if (m)
+ if (m) {
arg_shutdown_watchdog = m->shutdown_watchdog;
+ shutdown_exit_code = m->return_value;
+ }
m = manager_free(m);
for (j = 0; j < ELEMENTSOF(arg_default_rlimit); j++)
@@ -1978,7 +1978,8 @@ finish:
if (shutdown_verb) {
char log_level[DECIMAL_STR_MAX(int) + 1];
- const char* command_line[9] = {
+ char exit_code[DECIMAL_STR_MAX(uint8_t) + 1];
+ const char* command_line[11] = {
SYSTEMD_SHUTDOWN_BINARY_PATH,
shutdown_verb,
"--log-level", log_level,
@@ -2015,6 +2016,12 @@ finish:
if (log_get_show_location())
command_line[pos++] = "--log-location";
+ if (streq(shutdown_verb, "exit")) {
+ command_line[pos++] = "--exit-code";
+ command_line[pos++] = exit_code;
+ xsprintf(exit_code, "%d", shutdown_exit_code);
+ }
+
assert(pos < ELEMENTSOF(command_line));
if (arm_reboot_watchdog && arg_shutdown_watchdog > 0) {
diff --git a/src/core/manager.h b/src/core/manager.h
index b955982100..1384eb33a4 100644
--- a/src/core/manager.h
+++ b/src/core/manager.h
@@ -242,6 +242,11 @@ struct Manager {
bool test_run:1;
+ /* If non-zero, exit with the following value when the systemd
+ * process terminate. Useful for containers: systemd-nspawn could get
+ * the return value. */
+ uint8_t return_value;
+
ShowStatus show_status;
bool confirm_spawn;
bool no_console_output;
diff --git a/src/core/shutdown.c b/src/core/shutdown.c
index 8cc6efc5b8..5296efce1d 100644
--- a/src/core/shutdown.c
+++ b/src/core/shutdown.c
@@ -48,6 +48,7 @@
#define FINALIZE_ATTEMPTS 50
static char* arg_verb;
+static uint8_t arg_exit_code;
static int parse_argv(int argc, char *argv[]) {
enum {
@@ -55,6 +56,7 @@ static int parse_argv(int argc, char *argv[]) {
ARG_LOG_TARGET,
ARG_LOG_COLOR,
ARG_LOG_LOCATION,
+ ARG_EXIT_CODE,
};
static const struct option options[] = {
@@ -62,6 +64,7 @@ static int parse_argv(int argc, char *argv[]) {
{ "log-target", required_argument, NULL, ARG_LOG_TARGET },
{ "log-color", optional_argument, NULL, ARG_LOG_COLOR },
{ "log-location", optional_argument, NULL, ARG_LOG_LOCATION },
+ { "exit-code", required_argument, NULL, ARG_EXIT_CODE },
{}
};
@@ -110,6 +113,13 @@ static int parse_argv(int argc, char *argv[]) {
break;
+ case ARG_EXIT_CODE:
+ r = safe_atou8(optarg, &arg_exit_code);
+ if (r < 0)
+ log_error("Failed to parse exit code %s, ignoring", optarg);
+
+ break;
+
case '\001':
if (!arg_verb)
arg_verb = optarg;
@@ -183,6 +193,8 @@ int main(int argc, char *argv[]) {
cmd = RB_HALT_SYSTEM;
else if (streq(arg_verb, "kexec"))
cmd = LINUX_REBOOT_CMD_KEXEC;
+ else if (streq(arg_verb, "exit"))
+ cmd = 0; /* ignored, just checking that arg_verb is valid */
else {
r = -EINVAL;
log_error("Unknown action '%s'.", arg_verb);
@@ -339,6 +351,16 @@ int main(int argc, char *argv[]) {
if (!in_container)
sync();
+ if (streq(arg_verb, "exit")) {
+ if (in_container)
+ exit(arg_exit_code);
+ else {
+ /* We cannot exit() on the host, fallback on another
+ * method. */
+ cmd = RB_POWER_OFF;
+ }
+ }
+
switch (cmd) {
case LINUX_REBOOT_CMD_KEXEC:
diff --git a/src/core/unit.c b/src/core/unit.c
index 3356b97522..3a6313e4a2 100644
--- a/src/core/unit.c
+++ b/src/core/unit.c
@@ -3064,32 +3064,39 @@ int unit_kill_common(
sd_bus_error *error) {
int r = 0;
+ bool killed = false;
- if (who == KILL_MAIN) {
+ if (IN_SET(who, KILL_MAIN, KILL_MAIN_FAIL)) {
if (main_pid < 0)
return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_PROCESS, "%s units have no main processes", unit_type_to_string(u->type));
else if (main_pid == 0)
return sd_bus_error_set_const(error, BUS_ERROR_NO_SUCH_PROCESS, "No main process to kill");
}
- if (who == KILL_CONTROL) {
+ if (IN_SET(who, KILL_CONTROL, KILL_CONTROL_FAIL)) {
if (control_pid < 0)
return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_PROCESS, "%s units have no control processes", unit_type_to_string(u->type));
else if (control_pid == 0)
return sd_bus_error_set_const(error, BUS_ERROR_NO_SUCH_PROCESS, "No control process to kill");
}
- if (who == KILL_CONTROL || who == KILL_ALL)
- if (control_pid > 0)
+ if (IN_SET(who, KILL_CONTROL, KILL_CONTROL_FAIL, KILL_ALL, KILL_ALL_FAIL))
+ if (control_pid > 0) {
if (kill(control_pid, signo) < 0)
r = -errno;
+ else
+ killed = true;
+ }
- if (who == KILL_MAIN || who == KILL_ALL)
- if (main_pid > 0)
+ if (IN_SET(who, KILL_MAIN, KILL_MAIN_FAIL, KILL_ALL, KILL_ALL_FAIL))
+ if (main_pid > 0) {
if (kill(main_pid, signo) < 0)
r = -errno;
+ else
+ killed = true;
+ }
- if (who == KILL_ALL && u->cgroup_path) {
+ if (IN_SET(who, KILL_ALL, KILL_ALL_FAIL) && u->cgroup_path) {
_cleanup_set_free_ Set *pid_set = NULL;
int q;
@@ -3101,8 +3108,13 @@ int unit_kill_common(
q = cg_kill_recursive(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, signo, false, false, false, pid_set);
if (q < 0 && q != -EAGAIN && q != -ESRCH && q != -ENOENT)
r = q;
+ else
+ killed = true;
}
+ if (r == 0 && !killed && IN_SET(who, KILL_ALL_FAIL, KILL_CONTROL_FAIL, KILL_ALL_FAIL))
+ return -ESRCH;
+
return r;
}
diff --git a/src/libsystemd-network/arp-util.c b/src/libsystemd-network/arp-util.c
new file mode 100644
index 0000000000..2f5b9b3731
--- /dev/null
+++ b/src/libsystemd-network/arp-util.c
@@ -0,0 +1,153 @@
+/***
+ This file is part of systemd.
+
+ Copyright (C) 2014 Axis Communications AB. All rights reserved.
+ Copyright (C) 2015 Tom Gundersen
+
+ systemd 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.
+
+ systemd 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 systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <linux/filter.h>
+#include <arpa/inet.h>
+
+#include "util.h"
+#include "arp-util.h"
+
+int arp_network_bind_raw_socket(int ifindex, be32_t address, const struct ether_addr *eth_mac) {
+ struct sock_filter filter[] = {
+ BPF_STMT(BPF_LD + BPF_W + BPF_LEN, 0), /* A <- packet length */
+ BPF_JUMP(BPF_JMP + BPF_JGE + BPF_K, sizeof(struct ether_arp), 1, 0), /* packet >= arp packet ? */
+ BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */
+ BPF_STMT(BPF_LD + BPF_H + BPF_ABS, offsetof(struct ether_arp, ea_hdr.ar_hrd)), /* A <- header */
+ BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ARPHRD_ETHER, 1, 0), /* header == ethernet ? */
+ BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */
+ BPF_STMT(BPF_LD + BPF_H + BPF_ABS, offsetof(struct ether_arp, ea_hdr.ar_pro)), /* A <- protocol */
+ BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ETHERTYPE_IP, 1, 0), /* protocol == IP ? */
+ BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */
+ BPF_STMT(BPF_LD + BPF_B + BPF_ABS, offsetof(struct ether_arp, ea_hdr.ar_hln)), /* A <- hardware address length */
+ BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, sizeof(struct ether_addr), 1, 0), /* length == sizeof(ether_addr)? */
+ BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */
+ BPF_STMT(BPF_LD + BPF_B + BPF_ABS, offsetof(struct ether_arp, ea_hdr.ar_pln)), /* A <- protocol address length */
+ BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, sizeof(struct in_addr), 1, 0), /* length == sizeof(in_addr) ? */
+ BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */
+ BPF_STMT(BPF_LD + BPF_H + BPF_ABS, offsetof(struct ether_arp, ea_hdr.ar_op)), /* A <- operation */
+ BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ARPOP_REQUEST, 2, 0), /* protocol == request ? */
+ BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ARPOP_REPLY, 1, 0), /* protocol == reply ? */
+ BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */
+ /* Sender Hardware Address must be different from our own */
+ BPF_STMT(BPF_LD + BPF_IMM, htobe32(*((uint32_t *) eth_mac))), /* A <- 4 bytes of client's MAC */
+ BPF_STMT(BPF_MISC + BPF_TAX, 0), /* X <- A */
+ BPF_STMT(BPF_LD + BPF_W + BPF_ABS, offsetof(struct ether_arp, arp_sha)), /* A <- 4 bytes of SHA */
+ BPF_STMT(BPF_ALU + BPF_XOR + BPF_X, 0), /* A xor X */
+ BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0, 0, 6), /* A == 0 ? */
+ BPF_STMT(BPF_LD + BPF_IMM, htobe16(*((uint16_t *) (((char *) eth_mac) + 4)))), /* A <- remainder of client's MAC */
+ BPF_STMT(BPF_MISC + BPF_TAX, 0), /* X <- A */
+ BPF_STMT(BPF_LD + BPF_H + BPF_ABS, offsetof(struct ether_arp, arp_sha) + 4), /* A <- remainder of SHA */
+ BPF_STMT(BPF_ALU + BPF_XOR + BPF_X, 0), /* A xor X */
+ BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0, 0, 1), /* A == 0 ? */
+ BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */
+ /* Sender Protocol Address or Target Protocol Address must be equal to the one we care about*/
+ BPF_STMT(BPF_LD + BPF_IMM, htobe32(address)), /* A <- clients IP */
+ BPF_STMT(BPF_MISC + BPF_TAX, 0), /* X <- A */
+ BPF_STMT(BPF_LD + BPF_W + BPF_ABS, offsetof(struct ether_arp, arp_spa)), /* A <- SPA */
+ BPF_STMT(BPF_ALU + BPF_XOR + BPF_X, 0), /* X xor A */
+ BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0, 0, 1), /* A == 0 ? */
+ BPF_STMT(BPF_RET + BPF_K, 65535), /* return all */
+ BPF_STMT(BPF_LD + BPF_IMM, htobe32(address)), /* A <- clients IP */
+ BPF_STMT(BPF_MISC + BPF_TAX, 0), /* X <- A */
+ BPF_STMT(BPF_LD + BPF_W + BPF_ABS, offsetof(struct ether_arp, arp_tpa)), /* A <- TPA */
+ BPF_STMT(BPF_ALU + BPF_XOR + BPF_X, 0), /* X xor A */
+ BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0, 0, 1), /* A == 0 ? */
+ BPF_STMT(BPF_RET + BPF_K, 65535), /* return all */
+ BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */
+ };
+ struct sock_fprog fprog = {
+ .len = ELEMENTSOF(filter),
+ .filter = (struct sock_filter*) filter
+ };
+ union sockaddr_union link = {
+ .ll.sll_family = AF_PACKET,
+ .ll.sll_protocol = htons(ETH_P_ARP),
+ .ll.sll_ifindex = ifindex,
+ .ll.sll_halen = ETH_ALEN,
+ .ll.sll_addr = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff },
+ };
+ _cleanup_close_ int s = -1;
+ int r;
+
+ assert(ifindex > 0);
+
+ s = socket(PF_PACKET, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0);
+ if (s < 0)
+ return -errno;
+
+ r = setsockopt(s, SOL_SOCKET, SO_ATTACH_FILTER, &fprog, sizeof(fprog));
+ if (r < 0)
+ return -errno;
+
+ r = bind(s, &link.sa, sizeof(link.ll));
+ if (r < 0)
+ return -errno;
+
+ r = s;
+ s = -1;
+
+ return r;
+}
+
+static int arp_send_packet(int fd, int ifindex,
+ be32_t pa, const struct ether_addr *ha,
+ bool announce) {
+ union sockaddr_union link = {
+ .ll.sll_family = AF_PACKET,
+ .ll.sll_protocol = htons(ETH_P_ARP),
+ .ll.sll_ifindex = ifindex,
+ .ll.sll_halen = ETH_ALEN,
+ .ll.sll_addr = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff },
+ };
+ struct ether_arp arp = {
+ .ea_hdr.ar_hrd = htons(ARPHRD_ETHER), /* HTYPE */
+ .ea_hdr.ar_pro = htons(ETHERTYPE_IP), /* PTYPE */
+ .ea_hdr.ar_hln = ETH_ALEN, /* HLEN */
+ .ea_hdr.ar_pln = sizeof(be32_t), /* PLEN */
+ .ea_hdr.ar_op = htons(ARPOP_REQUEST), /* REQUEST */
+ };
+ int r;
+
+ assert(fd >= 0);
+ assert(pa != 0);
+ assert(ha);
+
+ memcpy(&arp.arp_sha, ha, ETH_ALEN);
+ memcpy(&arp.arp_tpa, &pa, sizeof(pa));
+
+ if (announce)
+ memcpy(&arp.arp_spa, &pa, sizeof(pa));
+
+ r = sendto(fd, &arp, sizeof(struct ether_arp), 0, &link.sa, sizeof(link.ll));
+ if (r < 0)
+ return -errno;
+
+ return 0;
+}
+
+int arp_send_probe(int fd, int ifindex,
+ be32_t pa, const struct ether_addr *ha) {
+ return arp_send_packet(fd, ifindex, pa, ha, false);
+}
+
+int arp_send_announcement(int fd, int ifindex,
+ be32_t pa, const struct ether_addr *ha) {
+ return arp_send_packet(fd, ifindex, pa, ha, true);
+}
diff --git a/src/libsystemd-network/ipv4ll-internal.h b/src/libsystemd-network/arp-util.h
index ae0ce43985..44e5c893a7 100644
--- a/src/libsystemd-network/ipv4ll-internal.h
+++ b/src/libsystemd-network/arp-util.h
@@ -26,13 +26,9 @@
#include "sparse-endian.h"
#include "socket-util.h"
-int arp_network_bind_raw_socket(int index, union sockaddr_union *link);
-int arp_network_send_raw_socket(int fd, const union sockaddr_union *link,
- const struct ether_arp *arp);
+int arp_network_bind_raw_socket(int index, be32_t address, const struct ether_addr *eth_mac);
-void arp_packet_init(struct ether_arp *arp);
-void arp_packet_probe(struct ether_arp *arp, be32_t pa, const struct ether_addr *ha);
-void arp_packet_announcement(struct ether_arp *arp, be32_t pa, const struct ether_addr *ha);
-int arp_packet_verify_headers(struct ether_arp *arp);
-
-#define log_ipv4ll(ll, fmt, ...) log_internal(LOG_DEBUG, 0, __FILE__, __LINE__, __func__, "IPv4LL: " fmt, ##__VA_ARGS__)
+int arp_send_probe(int fd, int ifindex,
+ be32_t pa, const struct ether_addr *ha);
+int arp_send_announcement(int fd, int ifindex,
+ be32_t pa, const struct ether_addr *ha);
diff --git a/src/libsystemd-network/ipv4ll-network.c b/src/libsystemd-network/ipv4ll-network.c
deleted file mode 100644
index 93ffed408f..0000000000
--- a/src/libsystemd-network/ipv4ll-network.c
+++ /dev/null
@@ -1,91 +0,0 @@
-/***
- This file is part of systemd.
-
- Copyright (C) 2014 Axis Communications AB. All rights reserved.
-
- systemd 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.
-
- systemd 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 systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include <linux/filter.h>
-
-#include "util.h"
-#include "ipv4ll-internal.h"
-
-int arp_network_send_raw_socket(int fd, const union sockaddr_union *link,
- const struct ether_arp *arp) {
- int r;
-
- assert(arp);
- assert(link);
- assert(fd >= 0);
-
- r = sendto(fd, arp, sizeof(struct ether_arp), 0, &link->sa, sizeof(link->ll));
- if (r < 0)
- return -errno;
-
- return 0;
-}
-
-int arp_network_bind_raw_socket(int ifindex, union sockaddr_union *link) {
-
- static const struct sock_filter filter[] = {
- BPF_STMT(BPF_LD + BPF_W + BPF_LEN, 0), /* A <- packet length */
- BPF_JUMP(BPF_JMP + BPF_JGE + BPF_K, sizeof(struct ether_arp), 1, 0), /* packet >= arp packet ? */
- BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */
- BPF_STMT(BPF_LD + BPF_H + BPF_ABS, offsetof(struct ether_arp, ea_hdr.ar_hrd)), /* A <- header */
- BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ARPHRD_ETHER, 1, 0), /* header == ethernet ? */
- BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */
- BPF_STMT(BPF_LD + BPF_H + BPF_ABS, offsetof(struct ether_arp, ea_hdr.ar_pro)), /* A <- protocol */
- BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ETHERTYPE_IP, 1, 0), /* protocol == IP ? */
- BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */
- BPF_STMT(BPF_LD + BPF_H + BPF_ABS, offsetof(struct ether_arp, ea_hdr.ar_op)), /* A <- operation */
- BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ARPOP_REQUEST, 0, 1), /* protocol == request ? */
- BPF_STMT(BPF_RET + BPF_K, 65535), /* return all */
- BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ARPOP_REPLY, 0, 1), /* protocol == reply ? */
- BPF_STMT(BPF_RET + BPF_K, 65535), /* return all */
- BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */
- };
- struct sock_fprog fprog = {
- .len = ELEMENTSOF(filter),
- .filter = (struct sock_filter*) filter
- };
- _cleanup_close_ int s = -1;
- int r;
-
- assert(ifindex > 0);
- assert(link);
-
- s = socket(PF_PACKET, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0);
- if (s < 0)
- return -errno;
-
- r = setsockopt(s, SOL_SOCKET, SO_ATTACH_FILTER, &fprog, sizeof(fprog));
- if (r < 0)
- return -errno;
-
- link->ll.sll_family = AF_PACKET;
- link->ll.sll_protocol = htons(ETH_P_ARP);
- link->ll.sll_ifindex = ifindex;
- link->ll.sll_halen = ETH_ALEN;
- memset(link->ll.sll_addr, 0xff, ETH_ALEN);
-
- r = bind(s, &link->sa, sizeof(link->ll));
- if (r < 0)
- return -errno;
-
- r = s;
- s = -1;
-
- return r;
-}
diff --git a/src/libsystemd-network/ipv4ll-packet.c b/src/libsystemd-network/ipv4ll-packet.c
deleted file mode 100644
index 2b6c73ab4b..0000000000
--- a/src/libsystemd-network/ipv4ll-packet.c
+++ /dev/null
@@ -1,71 +0,0 @@
-/***
- This file is part of systemd.
-
- Copyright (C) 2014 Axis Communications AB. All rights reserved.
-
- systemd 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.
-
- systemd 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 systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-#include <arpa/inet.h>
-
-#include "util.h"
-#include "ipv4ll-internal.h"
-
-void arp_packet_init(struct ether_arp *arp) {
- assert(arp);
-
- memzero(arp, sizeof(struct ether_arp));
- /* Header */
- arp->ea_hdr.ar_hrd = htons(ARPHRD_ETHER); /* HTYPE */
- arp->ea_hdr.ar_pro = htons(ETHERTYPE_IP); /* PTYPE */
- arp->ea_hdr.ar_hln = ETH_ALEN; /* HLEN */
- arp->ea_hdr.ar_pln = sizeof arp->arp_spa; /* PLEN */
- arp->ea_hdr.ar_op = htons(ARPOP_REQUEST); /* REQUEST */
-}
-
-void arp_packet_probe(struct ether_arp *arp, be32_t pa, const struct ether_addr *ha) {
- assert(ha);
-
- arp_packet_init(arp);
- memcpy(arp->arp_sha, ha, ETH_ALEN);
- memcpy(arp->arp_tpa, &pa, sizeof(pa));
-}
-
-void arp_packet_announcement(struct ether_arp *arp, be32_t pa, const struct ether_addr *ha) {
- assert(ha);
-
- arp_packet_init(arp);
- memcpy(arp->arp_sha, ha, ETH_ALEN);
- memcpy(arp->arp_tpa, &pa, sizeof(pa));
- memcpy(arp->arp_spa, &pa, sizeof(pa));
-}
-
-int arp_packet_verify_headers(struct ether_arp *arp) {
- assert(arp);
-
- if (arp->ea_hdr.ar_hrd != htons(ARPHRD_ETHER)) {
- log_ipv4ll(NULL, "ignoring packet: header is not ARPHRD_ETHER");
- return -EINVAL;
- }
- if (arp->ea_hdr.ar_pro != htons(ETHERTYPE_IP)) {
- log_ipv4ll(NULL, "ignoring packet: protocol is not ETHERTYPE_IP");
- return -EINVAL;
- }
- if (arp->ea_hdr.ar_op != htons(ARPOP_REQUEST) &&
- arp->ea_hdr.ar_op != htons(ARPOP_REPLY)) {
- log_ipv4ll(NULL, "ignoring packet: operation is not ARPOP_REQUEST or ARPOP_REPLY");
- return -EINVAL;
- }
-
- return 0;
-}
diff --git a/src/libsystemd-network/sd-ipv4acd.c b/src/libsystemd-network/sd-ipv4acd.c
new file mode 100644
index 0000000000..ee5d13c030
--- /dev/null
+++ b/src/libsystemd-network/sd-ipv4acd.c
@@ -0,0 +1,529 @@
+/***
+ This file is part of systemd.
+
+ Copyright (C) 2014 Axis Communications AB. All rights reserved.
+ Copyright (C) 2015 Tom Gundersen
+
+ systemd 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.
+
+ systemd 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 systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <arpa/inet.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "event-util.h"
+#include "in-addr-util.h"
+#include "list.h"
+#include "refcnt.h"
+#include "random-util.h"
+#include "siphash24.h"
+#include "util.h"
+
+#include "arp-util.h"
+#include "sd-ipv4acd.h"
+
+/* Constants from the RFC */
+#define PROBE_WAIT 1
+#define PROBE_NUM 3
+#define PROBE_MIN 1
+#define PROBE_MAX 2
+#define ANNOUNCE_WAIT 2
+#define ANNOUNCE_NUM 2
+#define ANNOUNCE_INTERVAL 2
+#define MAX_CONFLICTS 10
+#define RATE_LIMIT_INTERVAL 60
+#define DEFEND_INTERVAL 10
+
+#define IPV4ACD_NETWORK 0xA9FE0000L
+#define IPV4ACD_NETMASK 0xFFFF0000L
+
+#define log_ipv4acd_full(ll, level, error, fmt, ...) log_internal(level, error, __FILE__, __LINE__, __func__, "ACD: " fmt, ##__VA_ARGS__)
+
+#define log_ipv4acd_debug(ll, ...) log_ipv4acd_full(ll, LOG_DEBUG, 0, ##__VA_ARGS__)
+#define log_ipv4acd_info(ll, ...) log_ipv4acd_full(ll, LOG_INFO, 0, ##__VA_ARGS__)
+#define log_ipv4acd_notice(ll, ...) log_ipv4acd_full(ll, LOG_NOTICE, 0, ##__VA_ARGS__)
+#define log_ipv4acd_warning(ll, ...) log_ipv4acd_full(ll, LOG_WARNING, 0, ##__VA_ARGS__)
+#define log_ipv4acd_error(ll, ...) log_ipv4acd_full(ll, LOG_ERR, 0, ##__VA_ARGS__)
+
+#define log_ipv4acd_debug_errno(ll, error, ...) log_ipv4acd_full(ll, LOG_DEBUG, error, ##__VA_ARGS__)
+#define log_ipv4acd_info_errno(ll, error, ...) log_ipv4acd_full(ll, LOG_INFO, error, ##__VA_ARGS__)
+#define log_ipv4acd_notice_errno(ll, error, ...) log_ipv4acd_full(ll, LOG_NOTICE, error, ##__VA_ARGS__)
+#define log_ipv4acd_warning_errno(ll, error, ...) log_ipv4acd_full(ll, LOG_WARNING, error, ##__VA_ARGS__)
+#define log_ipv4acd_error_errno(ll, error, ...) log_ipv4acd_full(ll, LOG_ERR, error, ##__VA_ARGS__)
+
+typedef enum IPv4ACDState {
+ IPV4ACD_STATE_INIT,
+ IPV4ACD_STATE_WAITING_PROBE,
+ IPV4ACD_STATE_PROBING,
+ IPV4ACD_STATE_WAITING_ANNOUNCE,
+ IPV4ACD_STATE_ANNOUNCING,
+ IPV4ACD_STATE_RUNNING,
+ _IPV4ACD_STATE_MAX,
+ _IPV4ACD_STATE_INVALID = -1
+} IPv4ACDState;
+
+struct sd_ipv4acd {
+ RefCount n_ref;
+
+ IPv4ACDState state;
+ int index;
+ int fd;
+ int iteration;
+ int conflict;
+ sd_event_source *receive_message;
+ sd_event_source *timer;
+ usec_t defend_window;
+ be32_t address;
+ /* External */
+ struct ether_addr mac_addr;
+ sd_event *event;
+ int event_priority;
+ sd_ipv4acd_cb_t cb;
+ void* userdata;
+};
+
+sd_ipv4acd *sd_ipv4acd_ref(sd_ipv4acd *ll) {
+ if (ll)
+ assert_se(REFCNT_INC(ll->n_ref) >= 2);
+
+ return ll;
+}
+
+sd_ipv4acd *sd_ipv4acd_unref(sd_ipv4acd *ll) {
+ if (!ll || REFCNT_DEC(ll->n_ref) > 0)
+ return NULL;
+
+ ll->receive_message = sd_event_source_unref(ll->receive_message);
+ ll->fd = safe_close(ll->fd);
+
+ ll->timer = sd_event_source_unref(ll->timer);
+
+ sd_ipv4acd_detach_event(ll);
+
+ free(ll);
+
+ return NULL;
+}
+
+DEFINE_TRIVIAL_CLEANUP_FUNC(sd_ipv4acd*, sd_ipv4acd_unref);
+#define _cleanup_ipv4acd_unref_ _cleanup_(sd_ipv4acd_unrefp)
+
+int sd_ipv4acd_new(sd_ipv4acd **ret) {
+ _cleanup_ipv4acd_unref_ sd_ipv4acd *ll = NULL;
+
+ assert_return(ret, -EINVAL);
+
+ ll = new0(sd_ipv4acd, 1);
+ if (!ll)
+ return -ENOMEM;
+
+ ll->n_ref = REFCNT_INIT;
+ ll->state = IPV4ACD_STATE_INIT;
+ ll->index = -1;
+ ll->fd = -1;
+
+ *ret = ll;
+ ll = NULL;
+
+ return 0;
+}
+
+static void ipv4acd_set_state(sd_ipv4acd *ll, IPv4ACDState st, bool reset_counter) {
+
+ assert(ll);
+ assert(st < _IPV4ACD_STATE_MAX);
+
+ if (st == ll->state && !reset_counter)
+ ll->iteration++;
+ else {
+ ll->state = st;
+ ll->iteration = 0;
+ }
+}
+
+static void ipv4acd_client_notify(sd_ipv4acd *ll, int event) {
+ assert(ll);
+
+ if (ll->cb)
+ ll->cb(ll, event, ll->userdata);
+}
+
+static void ipv4acd_stop(sd_ipv4acd *ll) {
+ assert(ll);
+
+ ll->receive_message = sd_event_source_unref(ll->receive_message);
+ ll->fd = safe_close(ll->fd);
+
+ ll->timer = sd_event_source_unref(ll->timer);
+
+ log_ipv4acd_debug(ll, "STOPPED");
+
+ ipv4acd_set_state (ll, IPV4ACD_STATE_INIT, true);
+}
+
+int sd_ipv4acd_stop(sd_ipv4acd *ll) {
+ assert_return(ll, -EINVAL);
+
+ ipv4acd_stop(ll);
+
+ ipv4acd_client_notify(ll, IPV4ACD_EVENT_STOP);
+
+ return 0;
+}
+
+static int ipv4acd_on_timeout(sd_event_source *s, uint64_t usec, void *userdata);
+
+static int ipv4acd_set_next_wakeup(sd_ipv4acd *ll, int sec, int random_sec) {
+ _cleanup_event_source_unref_ sd_event_source *timer = NULL;
+ usec_t next_timeout;
+ usec_t time_now;
+ int r;
+
+ assert(sec >= 0);
+ assert(random_sec >= 0);
+ assert(ll);
+
+ next_timeout = sec * USEC_PER_SEC;
+
+ if (random_sec)
+ next_timeout += random_u32() % (random_sec * USEC_PER_SEC);
+
+ assert_se(sd_event_now(ll->event, clock_boottime_or_monotonic(), &time_now) >= 0);
+
+ r = sd_event_add_time(ll->event, &timer, clock_boottime_or_monotonic(),
+ time_now + next_timeout, 0, ipv4acd_on_timeout, ll);
+ if (r < 0)
+ return r;
+
+ r = sd_event_source_set_priority(timer, ll->event_priority);
+ if (r < 0)
+ return r;
+
+ r = sd_event_source_set_description(timer, "ipv4acd-timer");
+ if (r < 0)
+ return r;
+
+ ll->timer = sd_event_source_unref(ll->timer);
+ ll->timer = timer;
+ timer = NULL;
+
+ return 0;
+}
+
+static bool ipv4acd_arp_conflict(sd_ipv4acd *ll, struct ether_arp *arp) {
+ assert(ll);
+ assert(arp);
+
+ /* see the BPF */
+ if (memcmp(arp->arp_spa, &ll->address, sizeof(ll->address)) == 0)
+ return true;
+
+ /* the TPA matched instead of the SPA, this is not a conflict */
+ return false;
+}
+
+static int ipv4acd_on_timeout(sd_event_source *s, uint64_t usec, void *userdata) {
+ sd_ipv4acd *ll = userdata;
+ int r = 0;
+
+ assert(ll);
+
+ switch (ll->state) {
+ case IPV4ACD_STATE_INIT:
+
+ ipv4acd_set_state(ll, IPV4ACD_STATE_WAITING_PROBE, true);
+
+ if (ll->conflict >= MAX_CONFLICTS) {
+ log_ipv4acd_notice(ll, "Max conflicts reached, delaying by %us", RATE_LIMIT_INTERVAL);
+ r = ipv4acd_set_next_wakeup(ll, RATE_LIMIT_INTERVAL, PROBE_WAIT);
+ if (r < 0)
+ goto out;
+
+ ll->conflict = 0;
+ } else {
+ r = ipv4acd_set_next_wakeup(ll, 0, PROBE_WAIT);
+ if (r < 0)
+ goto out;
+ }
+
+ break;
+ case IPV4ACD_STATE_WAITING_PROBE:
+ case IPV4ACD_STATE_PROBING:
+ /* Send a probe */
+ r = arp_send_probe(ll->fd, ll->index, ll->address, &ll->mac_addr);
+ if (r < 0) {
+ log_ipv4acd_error_errno(ll, r, "Failed to send ARP probe: %m");
+ goto out;
+ } else {
+ _cleanup_free_ char *address = NULL;
+ union in_addr_union addr = { .in.s_addr = ll->address };
+
+ r = in_addr_to_string(AF_INET, &addr, &address);
+ if (r >= 0)
+ log_ipv4acd_debug(ll, "Probing %s", address);
+ }
+
+ if (ll->iteration < PROBE_NUM - 2) {
+ ipv4acd_set_state(ll, IPV4ACD_STATE_PROBING, false);
+
+ r = ipv4acd_set_next_wakeup(ll, PROBE_MIN, (PROBE_MAX-PROBE_MIN));
+ if (r < 0)
+ goto out;
+ } else {
+ ipv4acd_set_state(ll, IPV4ACD_STATE_WAITING_ANNOUNCE, true);
+
+ r = ipv4acd_set_next_wakeup(ll, ANNOUNCE_WAIT, 0);
+ if (r < 0)
+ goto out;
+ }
+
+ break;
+
+ case IPV4ACD_STATE_ANNOUNCING:
+ if (ll->iteration >= ANNOUNCE_NUM - 1) {
+ ipv4acd_set_state(ll, IPV4ACD_STATE_RUNNING, false);
+
+ break;
+ }
+ case IPV4ACD_STATE_WAITING_ANNOUNCE:
+ /* Send announcement packet */
+ r = arp_send_announcement(ll->fd, ll->index, ll->address, &ll->mac_addr);
+ if (r < 0) {
+ log_ipv4acd_error_errno(ll, r, "Failed to send ARP announcement: %m");
+ goto out;
+ } else
+ log_ipv4acd_debug(ll, "ANNOUNCE");
+
+ ipv4acd_set_state(ll, IPV4ACD_STATE_ANNOUNCING, false);
+
+ r = ipv4acd_set_next_wakeup(ll, ANNOUNCE_INTERVAL, 0);
+ if (r < 0)
+ goto out;
+
+ if (ll->iteration == 0) {
+ ll->conflict = 0;
+ ipv4acd_client_notify(ll, IPV4ACD_EVENT_BIND);
+ }
+
+ break;
+ default:
+ assert_not_reached("Invalid state.");
+ }
+
+out:
+ if (r < 0)
+ sd_ipv4acd_stop(ll);
+
+ return 1;
+}
+
+static void ipv4acd_on_conflict(sd_ipv4acd *ll) {
+ _cleanup_free_ char *address = NULL;
+ union in_addr_union addr = { .in.s_addr = ll->address };
+ int r;
+
+ assert(ll);
+
+ ll->conflict++;
+
+ r = in_addr_to_string(AF_INET, &addr, &address);
+ if (r >= 0)
+ log_ipv4acd_debug(ll, "Conflict on %s (%u)", address, ll->conflict);
+
+ ipv4acd_stop(ll);
+
+ ipv4acd_client_notify(ll, IPV4ACD_EVENT_CONFLICT);
+}
+
+static int ipv4acd_on_packet(sd_event_source *s, int fd,
+ uint32_t revents, void *userdata) {
+ sd_ipv4acd *ll = userdata;
+ struct ether_arp packet;
+ int r;
+
+ assert(ll);
+ assert(fd >= 0);
+
+ r = read(fd, &packet, sizeof(struct ether_arp));
+ if (r < (int) sizeof(struct ether_arp))
+ goto out;
+
+ switch (ll->state) {
+ case IPV4ACD_STATE_ANNOUNCING:
+ case IPV4ACD_STATE_RUNNING:
+ if (ipv4acd_arp_conflict(ll, &packet)) {
+ usec_t ts;
+
+ assert_se(sd_event_now(ll->event, clock_boottime_or_monotonic(), &ts) >= 0);
+
+ /* Defend address */
+ if (ts > ll->defend_window) {
+ ll->defend_window = ts + DEFEND_INTERVAL * USEC_PER_SEC;
+ r = arp_send_announcement(ll->fd, ll->index, ll->address, &ll->mac_addr);
+ if (r < 0) {
+ log_ipv4acd_error_errno(ll, r, "Failed to send ARP announcement: %m");
+ goto out;
+ } else
+ log_ipv4acd_debug(ll, "DEFEND");
+
+ } else
+ ipv4acd_on_conflict(ll);
+ }
+
+ break;
+ case IPV4ACD_STATE_WAITING_PROBE:
+ case IPV4ACD_STATE_PROBING:
+ case IPV4ACD_STATE_WAITING_ANNOUNCE:
+ /* BPF ensures this packet indicates a conflict */
+ ipv4acd_on_conflict(ll);
+
+ break;
+ default:
+ assert_not_reached("Invalid state.");
+ }
+
+out:
+ if (r < 0)
+ sd_ipv4acd_stop(ll);
+
+ return 1;
+}
+
+int sd_ipv4acd_set_index(sd_ipv4acd *ll, int interface_index) {
+ assert_return(ll, -EINVAL);
+ assert_return(interface_index > 0, -EINVAL);
+ assert_return(ll->state == IPV4ACD_STATE_INIT, -EBUSY);
+
+ ll->index = interface_index;
+
+ return 0;
+}
+
+int sd_ipv4acd_set_mac(sd_ipv4acd *ll, const struct ether_addr *addr) {
+ assert_return(ll, -EINVAL);
+ assert_return(addr, -EINVAL);
+ assert_return(ll->state == IPV4ACD_STATE_INIT, -EBUSY);
+
+ memcpy(&ll->mac_addr, addr, ETH_ALEN);
+
+ return 0;
+}
+
+int sd_ipv4acd_detach_event(sd_ipv4acd *ll) {
+ assert_return(ll, -EINVAL);
+
+ ll->event = sd_event_unref(ll->event);
+
+ return 0;
+}
+
+int sd_ipv4acd_attach_event(sd_ipv4acd *ll, sd_event *event, int priority) {
+ int r;
+
+ assert_return(ll, -EINVAL);
+ assert_return(!ll->event, -EBUSY);
+
+ if (event)
+ ll->event = sd_event_ref(event);
+ else {
+ r = sd_event_default(&ll->event);
+ if (r < 0)
+ return r;
+ }
+
+ ll->event_priority = priority;
+
+ return 0;
+}
+
+int sd_ipv4acd_set_callback(sd_ipv4acd *ll, sd_ipv4acd_cb_t cb, void *userdata) {
+ assert_return(ll, -EINVAL);
+
+ ll->cb = cb;
+ ll->userdata = userdata;
+
+ return 0;
+}
+
+int sd_ipv4acd_set_address(sd_ipv4acd *ll, const struct in_addr *address){
+ assert_return(ll, -EINVAL);
+ assert_return(address, -EINVAL);
+ assert_return(ll->state == IPV4ACD_STATE_INIT, -EBUSY);
+
+ ll->address = address->s_addr;
+
+ return 0;
+}
+
+bool sd_ipv4acd_is_running(sd_ipv4acd *ll) {
+ assert_return(ll, false);
+
+ return ll->state != IPV4ACD_STATE_INIT;
+}
+
+static bool ether_addr_is_nul(const struct ether_addr *addr) {
+ const struct ether_addr nul_addr = {};
+
+ assert(addr);
+
+ return memcmp(addr, &nul_addr, sizeof(struct ether_addr)) == 0;
+}
+
+#define HASH_KEY SD_ID128_MAKE(df,04,22,98,3f,ad,14,52,f9,87,2e,d1,9c,70,e2,f2)
+
+int sd_ipv4acd_start(sd_ipv4acd *ll) {
+ int r;
+
+ assert_return(ll, -EINVAL);
+ assert_return(ll->event, -EINVAL);
+ assert_return(ll->index > 0, -EINVAL);
+ assert_return(ll->address != 0, -EINVAL);
+ assert_return(!ether_addr_is_nul(&ll->mac_addr), -EINVAL);
+ assert_return(ll->state == IPV4ACD_STATE_INIT, -EBUSY);
+
+ ll->defend_window = 0;
+
+ r = arp_network_bind_raw_socket(ll->index, ll->address, &ll->mac_addr);
+ if (r < 0)
+ goto out;
+
+ ll->fd = safe_close(ll->fd);
+ ll->fd = r;
+
+ r = sd_event_add_io(ll->event, &ll->receive_message, ll->fd,
+ EPOLLIN, ipv4acd_on_packet, ll);
+ if (r < 0)
+ goto out;
+
+ r = sd_event_source_set_priority(ll->receive_message, ll->event_priority);
+ if (r < 0)
+ goto out;
+
+ r = sd_event_source_set_description(ll->receive_message, "ipv4acd-receive-message");
+ if (r < 0)
+ goto out;
+
+ r = ipv4acd_set_next_wakeup(ll, 0, 0);
+ if (r < 0)
+ goto out;
+out:
+ if (r < 0) {
+ ipv4acd_stop(ll);
+ return r;
+ }
+
+ return 0;
+}
diff --git a/src/libsystemd-network/sd-ipv4ll.c b/src/libsystemd-network/sd-ipv4ll.c
index 14b9444dab..f0230b919c 100644
--- a/src/libsystemd-network/sd-ipv4ll.c
+++ b/src/libsystemd-network/sd-ipv4ll.c
@@ -2,6 +2,7 @@
This file is part of systemd.
Copyright (C) 2014 Axis Communications AB. All rights reserved.
+ Copyright (C) 2015 Tom Gundersen
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
@@ -23,429 +24,153 @@
#include <stdio.h>
#include <arpa/inet.h>
-#include "util.h"
-#include "siphash24.h"
+#include "event-util.h"
#include "list.h"
#include "random-util.h"
+#include "refcnt.h"
+#include "siphash24.h"
+#include "sparse-endian.h"
+#include "util.h"
-#include "ipv4ll-internal.h"
+#include "sd-ipv4acd.h"
#include "sd-ipv4ll.h"
-/* Constants from the RFC */
-#define PROBE_WAIT 1
-#define PROBE_NUM 3
-#define PROBE_MIN 1
-#define PROBE_MAX 2
-#define ANNOUNCE_WAIT 2
-#define ANNOUNCE_NUM 2
-#define ANNOUNCE_INTERVAL 2
-#define MAX_CONFLICTS 10
-#define RATE_LIMIT_INTERVAL 60
-#define DEFEND_INTERVAL 10
-
#define IPV4LL_NETWORK 0xA9FE0000L
#define IPV4LL_NETMASK 0xFFFF0000L
-typedef enum IPv4LLTrigger{
- IPV4LL_TRIGGER_NULL,
- IPV4LL_TRIGGER_PACKET,
- IPV4LL_TRIGGER_TIMEOUT,
- _IPV4LL_TRIGGER_MAX,
- _IPV4LL_TRIGGER_INVALID = -1
-} IPv4LLTrigger;
-
-typedef enum IPv4LLState {
- IPV4LL_STATE_INIT,
- IPV4LL_STATE_WAITING_PROBE,
- IPV4LL_STATE_PROBING,
- IPV4LL_STATE_WAITING_ANNOUNCE,
- IPV4LL_STATE_ANNOUNCING,
- IPV4LL_STATE_RUNNING,
- IPV4LL_STATE_STOPPED,
- _IPV4LL_STATE_MAX,
- _IPV4LL_STATE_INVALID = -1
-} IPv4LLState;
+#define IPV4LL_DONT_DESTROY(ll) \
+ _cleanup_ipv4ll_unref_ _unused_ sd_ipv4ll *_dont_destroy_##ll = sd_ipv4ll_ref(ll)
struct sd_ipv4ll {
unsigned n_ref;
- IPv4LLState state;
- int index;
- int fd;
- union sockaddr_union link;
- int iteration;
- int conflict;
- sd_event_source *receive_message;
- sd_event_source *timer;
- usec_t next_wakeup;
- usec_t defend_window;
- int next_wakeup_valid;
- be32_t address;
+ sd_ipv4acd *acd;
+ be32_t address; /* the address pushed to ACD */
struct random_data *random_data;
char *random_data_state;
+
/* External */
be32_t claimed_address;
- struct ether_addr mac_addr;
- sd_event *event;
- int event_priority;
sd_ipv4ll_cb_t cb;
void* userdata;
};
-static void ipv4ll_run_state_machine(sd_ipv4ll *ll, IPv4LLTrigger trigger, void *trigger_data);
-
-static void ipv4ll_set_state(sd_ipv4ll *ll, IPv4LLState st, int reset_counter) {
-
- assert(ll);
- assert(st < _IPV4LL_STATE_MAX);
-
- if (st == ll->state && !reset_counter) {
- ll->iteration++;
- } else {
- ll->state = st;
- ll->iteration = 0;
- }
-}
-
-static sd_ipv4ll *ipv4ll_client_notify(sd_ipv4ll *ll, int event) {
- assert(ll);
-
- if (ll->cb) {
- ll = sd_ipv4ll_ref(ll);
- ll->cb(ll, event, ll->userdata);
- ll = sd_ipv4ll_unref(ll);
- }
-
- return ll;
-}
-
-static sd_ipv4ll *ipv4ll_stop(sd_ipv4ll *ll, int event) {
- assert(ll);
-
- ll->receive_message = sd_event_source_unref(ll->receive_message);
- ll->fd = safe_close(ll->fd);
-
- ll->timer = sd_event_source_unref(ll->timer);
-
- log_ipv4ll(ll, "STOPPED");
-
- ll = ipv4ll_client_notify(ll, event);
+sd_ipv4ll *sd_ipv4ll_ref(sd_ipv4ll *ll) {
+ if (!ll)
+ return NULL;
- if (ll) {
- ll->claimed_address = 0;
- ipv4ll_set_state (ll, IPV4LL_STATE_INIT, 1);
- }
+ assert(ll->n_ref >= 1);
+ ll->n_ref++;
return ll;
}
-static int ipv4ll_pick_address(sd_ipv4ll *ll, be32_t *address) {
- be32_t addr;
- int r;
- int32_t random;
-
- assert(ll);
- assert(address);
- assert(ll->random_data);
-
- do {
- r = random_r(ll->random_data, &random);
- if (r < 0)
- return r;
- addr = htonl((random & 0x0000FFFF) | IPV4LL_NETWORK);
- } while (addr == ll->address ||
- (ntohl(addr) & IPV4LL_NETMASK) != IPV4LL_NETWORK ||
- (ntohl(addr) & 0x0000FF00) == 0x0000 ||
- (ntohl(addr) & 0x0000FF00) == 0xFF00);
-
- *address = addr;
- return 0;
-}
-
-static int ipv4ll_timer(sd_event_source *s, uint64_t usec, void *userdata) {
- sd_ipv4ll *ll = (sd_ipv4ll*)userdata;
-
- assert(ll);
-
- ll->next_wakeup_valid = 0;
- ipv4ll_run_state_machine(ll, IPV4LL_TRIGGER_TIMEOUT, NULL);
-
- return 0;
-}
-
-static void ipv4ll_set_next_wakeup(sd_ipv4ll *ll, int sec, int random_sec) {
- usec_t next_timeout = 0;
- usec_t time_now = 0;
-
- assert(sec >= 0);
- assert(random_sec >= 0);
- assert(ll);
-
- next_timeout = sec * USEC_PER_SEC;
-
- if (random_sec)
- next_timeout += random_u32() % (random_sec * USEC_PER_SEC);
-
- assert_se(sd_event_now(ll->event, clock_boottime_or_monotonic(), &time_now) >= 0);
-
- ll->next_wakeup = time_now + next_timeout;
- ll->next_wakeup_valid = 1;
-}
-
-static bool ipv4ll_arp_conflict (sd_ipv4ll *ll, struct ether_arp *arp) {
- assert(ll);
- assert(arp);
-
- if (memcmp(arp->arp_spa, &ll->address, sizeof(ll->address)) == 0 &&
- memcmp(arp->arp_sha, &ll->mac_addr, ETH_ALEN) != 0)
- return true;
+sd_ipv4ll *sd_ipv4ll_unref(sd_ipv4ll *ll) {
+ if (!ll)
+ return NULL;
- return false;
-}
+ assert(ll->n_ref >= 1);
+ ll->n_ref--;
-static bool ipv4ll_arp_probe_conflict (sd_ipv4ll *ll, struct ether_arp *arp) {
- assert(ll);
- assert(arp);
+ if (ll->n_ref > 0)
+ return NULL;
- if (ipv4ll_arp_conflict(ll, arp))
- return true;
+ sd_ipv4acd_unref(ll->acd);
- if (memcmp(arp->arp_tpa, &ll->address, sizeof(ll->address)) == 0 &&
- memcmp(arp->arp_sha, &ll->mac_addr, ETH_ALEN))
- return true;
+ free(ll->random_data);
+ free(ll->random_data_state);
+ free(ll);
- return false;
+ return NULL;
}
-static void ipv4ll_run_state_machine(sd_ipv4ll *ll, IPv4LLTrigger trigger, void *trigger_data) {
- struct ether_arp out_packet;
- int out_packet_ready = 0;
- int r = 0;
-
- assert(ll);
- assert(trigger < _IPV4LL_TRIGGER_MAX);
-
- if (ll->state == IPV4LL_STATE_INIT) {
-
- log_ipv4ll(ll, "PROBE");
- ipv4ll_set_state(ll, IPV4LL_STATE_WAITING_PROBE, 1);
- ipv4ll_set_next_wakeup(ll, 0, PROBE_WAIT);
-
- } else if ((ll->state == IPV4LL_STATE_WAITING_PROBE && trigger == IPV4LL_TRIGGER_TIMEOUT) ||
- (ll->state == IPV4LL_STATE_PROBING && trigger == IPV4LL_TRIGGER_TIMEOUT && ll->iteration < PROBE_NUM-2)) {
-
- /* Send a probe */
- arp_packet_probe(&out_packet, ll->address, &ll->mac_addr);
- out_packet_ready = 1;
- ipv4ll_set_state(ll, IPV4LL_STATE_PROBING, 0);
-
- ipv4ll_set_next_wakeup(ll, PROBE_MIN, (PROBE_MAX-PROBE_MIN));
-
- } else if (ll->state == IPV4LL_STATE_PROBING && trigger == IPV4LL_TRIGGER_TIMEOUT && ll->iteration >= PROBE_NUM-2) {
-
- /* Send the last probe */
- arp_packet_probe(&out_packet, ll->address, &ll->mac_addr);
- out_packet_ready = 1;
- ipv4ll_set_state(ll, IPV4LL_STATE_WAITING_ANNOUNCE, 1);
-
- ipv4ll_set_next_wakeup(ll, ANNOUNCE_WAIT, 0);
-
- } else if ((ll->state == IPV4LL_STATE_WAITING_ANNOUNCE && trigger == IPV4LL_TRIGGER_TIMEOUT) ||
- (ll->state == IPV4LL_STATE_ANNOUNCING && trigger == IPV4LL_TRIGGER_TIMEOUT && ll->iteration < ANNOUNCE_NUM-1)) {
-
- /* Send announcement packet */
- arp_packet_announcement(&out_packet, ll->address, &ll->mac_addr);
- out_packet_ready = 1;
- ipv4ll_set_state(ll, IPV4LL_STATE_ANNOUNCING, 0);
-
- ipv4ll_set_next_wakeup(ll, ANNOUNCE_INTERVAL, 0);
-
- if (ll->iteration == 0) {
- log_ipv4ll(ll, "ANNOUNCE");
- ll->claimed_address = ll->address;
- ll = ipv4ll_client_notify(ll, IPV4LL_EVENT_BIND);
- if (!ll || ll->state == IPV4LL_STATE_STOPPED)
- goto out;
-
- ll->conflict = 0;
- }
-
- } else if ((ll->state == IPV4LL_STATE_ANNOUNCING && trigger == IPV4LL_TRIGGER_TIMEOUT &&
- ll->iteration >= ANNOUNCE_NUM-1)) {
-
- ipv4ll_set_state(ll, IPV4LL_STATE_RUNNING, 0);
- ll->next_wakeup_valid = 0;
-
- } else if (trigger == IPV4LL_TRIGGER_PACKET) {
-
- int conflicted = 0;
- usec_t time_now;
- struct ether_arp* in_packet = (struct ether_arp*)trigger_data;
-
- assert(in_packet);
-
- if (IN_SET(ll->state, IPV4LL_STATE_ANNOUNCING, IPV4LL_STATE_RUNNING)) {
-
- if (ipv4ll_arp_conflict(ll, in_packet)) {
-
- r = sd_event_now(ll->event, clock_boottime_or_monotonic(), &time_now);
- if (r < 0)
- goto out;
-
- /* Defend address */
- if (time_now > ll->defend_window) {
- ll->defend_window = time_now + DEFEND_INTERVAL * USEC_PER_SEC;
- arp_packet_announcement(&out_packet, ll->address, &ll->mac_addr);
- out_packet_ready = 1;
- } else
- conflicted = 1;
- }
-
- } else if (IN_SET(ll->state, IPV4LL_STATE_WAITING_PROBE,
- IPV4LL_STATE_PROBING,
- IPV4LL_STATE_WAITING_ANNOUNCE)) {
-
- conflicted = ipv4ll_arp_probe_conflict(ll, in_packet);
- }
-
- if (conflicted) {
- log_ipv4ll(ll, "CONFLICT");
- ll = ipv4ll_client_notify(ll, IPV4LL_EVENT_CONFLICT);
- if (!ll || ll->state == IPV4LL_STATE_STOPPED)
- goto out;
+DEFINE_TRIVIAL_CLEANUP_FUNC(sd_ipv4ll*, sd_ipv4ll_unref);
+#define _cleanup_ipv4ll_unref_ _cleanup_(sd_ipv4ll_unrefp)
- ll->claimed_address = 0;
+static void ipv4ll_on_acd(sd_ipv4acd *ll, int event, void *userdata);
- /* Pick a new address */
- r = ipv4ll_pick_address(ll, &ll->address);
- if (r < 0)
- goto out;
- ll->conflict++;
- ll->defend_window = 0;
- ipv4ll_set_state(ll, IPV4LL_STATE_WAITING_PROBE, 1);
+int sd_ipv4ll_new(sd_ipv4ll **ret) {
+ _cleanup_ipv4ll_unref_ sd_ipv4ll *ll = NULL;
+ int r;
- if (ll->conflict >= MAX_CONFLICTS) {
- log_ipv4ll(ll, "MAX_CONFLICTS");
- ipv4ll_set_next_wakeup(ll, RATE_LIMIT_INTERVAL, PROBE_WAIT);
- } else
- ipv4ll_set_next_wakeup(ll, 0, PROBE_WAIT);
+ assert_return(ret, -EINVAL);
- }
- }
+ ll = new0(sd_ipv4ll, 1);
+ if (!ll)
+ return -ENOMEM;
- if (out_packet_ready) {
- r = arp_network_send_raw_socket(ll->fd, &ll->link, &out_packet);
- if (r < 0) {
- log_ipv4ll(ll, "failed to send arp packet out");
- goto out;
- }
- }
+ r = sd_ipv4acd_new(&ll->acd);
+ if (r < 0)
+ return r;
- if (ll->next_wakeup_valid) {
- ll->timer = sd_event_source_unref(ll->timer);
- r = sd_event_add_time(ll->event, &ll->timer, clock_boottime_or_monotonic(),
- ll->next_wakeup, 0, ipv4ll_timer, ll);
- if (r < 0)
- goto out;
+ r = sd_ipv4acd_set_callback(ll->acd, ipv4ll_on_acd, ll);
+ if (r < 0)
+ return r;
- r = sd_event_source_set_priority(ll->timer, ll->event_priority);
- if (r < 0)
- goto out;
+ ll->n_ref = 1;
- r = sd_event_source_set_description(ll->timer, "ipv4ll-timer");
- if (r < 0)
- goto out;
- }
+ *ret = ll;
+ ll = NULL;
-out:
- if (r < 0 && ll)
- ipv4ll_stop(ll, r);
+ return 0;
}
-static int ipv4ll_receive_message(sd_event_source *s, int fd,
- uint32_t revents, void *userdata) {
+int sd_ipv4ll_stop(sd_ipv4ll *ll) {
int r;
- struct ether_arp arp;
- sd_ipv4ll *ll = (sd_ipv4ll*)userdata;
- assert(ll);
-
- r = read(fd, &arp, sizeof(struct ether_arp));
- if (r < (int) sizeof(struct ether_arp))
- return 0;
+ assert_return(ll, -EINVAL);
- r = arp_packet_verify_headers(&arp);
+ r = sd_ipv4acd_stop(ll->acd);
if (r < 0)
- return 0;
-
- ipv4ll_run_state_machine(ll, IPV4LL_TRIGGER_PACKET, &arp);
+ return r;
return 0;
}
int sd_ipv4ll_set_index(sd_ipv4ll *ll, int interface_index) {
assert_return(ll, -EINVAL);
- assert_return(interface_index > 0, -EINVAL);
- assert_return(IN_SET(ll->state, IPV4LL_STATE_INIT,
- IPV4LL_STATE_STOPPED), -EBUSY);
- ll->index = interface_index;
-
- return 0;
+ return sd_ipv4acd_set_index(ll->acd, interface_index);
}
+#define HASH_KEY SD_ID128_MAKE(df,04,22,98,3f,ad,14,52,f9,87,2e,d1,9c,70,e2,f2)
+
int sd_ipv4ll_set_mac(sd_ipv4ll *ll, const struct ether_addr *addr) {
- bool need_restart = false;
+ int r;
assert_return(ll, -EINVAL);
- assert_return(addr, -EINVAL);
-
- if (memcmp(&ll->mac_addr, addr, ETH_ALEN) == 0)
- return 0;
- if (!IN_SET(ll->state, IPV4LL_STATE_INIT, IPV4LL_STATE_STOPPED)) {
- log_ipv4ll(ll, "Changing MAC address on running IPv4LL "
- "client, restarting");
- ll = ipv4ll_stop(ll, IPV4LL_EVENT_STOP);
- need_restart = true;
- }
+ if (!ll->random_data) {
+ uint8_t seed[8];
- if (!ll)
- return 0;
+ /* If no random data is set, generate some from the MAC */
+ siphash24(seed, &addr->ether_addr_octet,
+ ETH_ALEN, HASH_KEY.bytes);
- memcpy(&ll->mac_addr, addr, ETH_ALEN);
+ assert_cc(sizeof(unsigned) <= 8);
- if (need_restart)
- sd_ipv4ll_start(ll);
+ r = sd_ipv4ll_set_address_seed(ll, *(unsigned*)seed);
+ if (r < 0)
+ return r;
+ }
- return 0;
+ return sd_ipv4acd_set_mac(ll->acd, addr);
}
int sd_ipv4ll_detach_event(sd_ipv4ll *ll) {
assert_return(ll, -EINVAL);
- ll->event = sd_event_unref(ll->event);
-
- return 0;
+ return sd_ipv4acd_detach_event(ll->acd);
}
int sd_ipv4ll_attach_event(sd_ipv4ll *ll, sd_event *event, int priority) {
int r;
assert_return(ll, -EINVAL);
- assert_return(!ll->event, -EBUSY);
-
- if (event)
- ll->event = sd_event_ref(event);
- else {
- r = sd_event_default(&ll->event);
- if (r < 0) {
- ipv4ll_stop(ll, IPV4LL_EVENT_STOP);
- return r;
- }
- }
- ll->event_priority = priority;
+ r = sd_ipv4acd_attach_event(ll->acd, event, priority);
+ if (r < 0)
+ return r;
return 0;
}
@@ -467,189 +192,147 @@ int sd_ipv4ll_get_address(sd_ipv4ll *ll, struct in_addr *address){
return -ENOENT;
address->s_addr = ll->claimed_address;
+
return 0;
}
-int sd_ipv4ll_set_address_seed (sd_ipv4ll *ll, uint8_t seed[8]) {
- unsigned int entropy;
+int sd_ipv4ll_set_address_seed(sd_ipv4ll *ll, unsigned seed) {
+ _cleanup_free_ struct random_data *random_data = NULL;
+ _cleanup_free_ char *random_data_state = NULL;
int r;
assert_return(ll, -EINVAL);
assert_return(seed, -EINVAL);
- entropy = *seed;
+ random_data = new0(struct random_data, 1);
+ if (!random_data)
+ return -ENOMEM;
- free(ll->random_data);
- free(ll->random_data_state);
+ random_data_state = new0(char, 128);
+ if (!random_data_state)
+ return -ENOMEM;
- ll->random_data = new0(struct random_data, 1);
- ll->random_data_state = new0(char, 128);
+ r = initstate_r(seed, random_data_state, 128, random_data);
+ if (r < 0)
+ return r;
- if (!ll->random_data || !ll->random_data_state) {
- r = -ENOMEM;
- goto error;
- }
+ free(ll->random_data);
+ ll->random_data = random_data;
+ random_data = NULL;
- r = initstate_r((unsigned int)entropy, ll->random_data_state, 128, ll->random_data);
- if (r < 0)
- goto error;
+ free(ll->random_data_state);
+ ll->random_data_state = random_data_state;
+ random_data_state = NULL;
-error:
- if (r < 0){
- free(ll->random_data);
- free(ll->random_data_state);
- ll->random_data = NULL;
- ll->random_data_state = NULL;
- }
- return r;
+ return 0;
}
bool sd_ipv4ll_is_running(sd_ipv4ll *ll) {
assert_return(ll, false);
- return !IN_SET(ll->state, IPV4LL_STATE_INIT, IPV4LL_STATE_STOPPED);
+ return sd_ipv4acd_is_running(ll->acd);
}
-#define HASH_KEY SD_ID128_MAKE(df,04,22,98,3f,ad,14,52,f9,87,2e,d1,9c,70,e2,f2)
-
-int sd_ipv4ll_start (sd_ipv4ll *ll) {
+static int ipv4ll_pick_address(sd_ipv4ll *ll) {
+ struct in_addr in_addr;
+ be32_t addr;
int r;
+ int32_t random;
- assert_return(ll, -EINVAL);
- assert_return(ll->event, -EINVAL);
- assert_return(ll->index > 0, -EINVAL);
- assert_return(IN_SET(ll->state, IPV4LL_STATE_INIT,
- IPV4LL_STATE_STOPPED), -EBUSY);
+ assert(ll);
+ assert(ll->random_data);
- ll->state = IPV4LL_STATE_INIT;
+ do {
+ r = random_r(ll->random_data, &random);
+ if (r < 0)
+ return r;
+ addr = htonl((random & 0x0000FFFF) | IPV4LL_NETWORK);
+ } while (addr == ll->address ||
+ (ntohl(addr) & IPV4LL_NETMASK) != IPV4LL_NETWORK ||
+ (ntohl(addr) & 0x0000FF00) == 0x0000 ||
+ (ntohl(addr) & 0x0000FF00) == 0xFF00);
- r = arp_network_bind_raw_socket(ll->index, &ll->link);
+ in_addr.s_addr = addr;
+ r = sd_ipv4acd_set_address(ll->acd, &in_addr);
if (r < 0)
- goto out;
+ return r;
- ll->fd = r;
- ll->conflict = 0;
- ll->defend_window = 0;
- ll->claimed_address = 0;
+ ll->address = addr;
- if (!ll->random_data) {
- uint8_t seed[8];
+ return 0;
+}
- /* Fallback to mac */
- siphash24(seed, &ll->mac_addr.ether_addr_octet,
- ETH_ALEN, HASH_KEY.bytes);
+int sd_ipv4ll_start(sd_ipv4ll *ll) {
+ int r;
- r = sd_ipv4ll_set_address_seed(ll, seed);
- if (r < 0)
- goto out;
- }
+ assert_return(ll, -EINVAL);
+ assert_return(ll->random_data, -EINVAL);
if (ll->address == 0) {
- r = ipv4ll_pick_address(ll, &ll->address);
+ r = ipv4ll_pick_address(ll);
if (r < 0)
- goto out;
+ return r;
}
- ipv4ll_set_state (ll, IPV4LL_STATE_INIT, 1);
-
- r = sd_event_add_io(ll->event, &ll->receive_message, ll->fd,
- EPOLLIN, ipv4ll_receive_message, ll);
+ r = sd_ipv4acd_start(ll->acd);
if (r < 0)
- goto out;
-
- r = sd_event_source_set_priority(ll->receive_message, ll->event_priority);
- if (r < 0)
- goto out;
-
- r = sd_event_source_set_description(ll->receive_message, "ipv4ll-receive-message");
- if (r < 0)
- goto out;
-
- r = sd_event_add_time(ll->event,
- &ll->timer,
- clock_boottime_or_monotonic(),
- now(clock_boottime_or_monotonic()), 0,
- ipv4ll_timer, ll);
-
- if (r < 0)
- goto out;
-
- r = sd_event_source_set_priority(ll->timer, ll->event_priority);
- if (r < 0)
- goto out;
-
- r = sd_event_source_set_description(ll->timer, "ipv4ll-timer");
-out:
- if (r < 0)
- ipv4ll_stop(ll, IPV4LL_EVENT_STOP);
+ return r;
return 0;
}
-int sd_ipv4ll_stop(sd_ipv4ll *ll) {
- ipv4ll_stop(ll, IPV4LL_EVENT_STOP);
- if (ll)
- ipv4ll_set_state(ll, IPV4LL_STATE_STOPPED, 1);
-
- return 0;
-}
-
-sd_ipv4ll *sd_ipv4ll_ref(sd_ipv4ll *ll) {
-
- if (!ll)
- return NULL;
-
- assert(ll->n_ref >= 1);
- ll->n_ref++;
+static void ipv4ll_client_notify(sd_ipv4ll *ll, int event) {
+ assert(ll);
- return ll;
+ if (ll->cb)
+ ll->cb(ll, event, ll->userdata);
}
-sd_ipv4ll *sd_ipv4ll_unref(sd_ipv4ll *ll) {
-
- if (!ll)
- return NULL;
-
- assert(ll->n_ref >= 1);
- ll->n_ref--;
-
- if (ll->n_ref > 0)
- return ll;
-
- ll->receive_message = sd_event_source_unref(ll->receive_message);
- ll->fd = safe_close(ll->fd);
-
- ll->timer = sd_event_source_unref(ll->timer);
+void ipv4ll_on_acd(sd_ipv4acd *acd, int event, void *userdata) {
+ sd_ipv4ll *ll = userdata;
+ IPV4LL_DONT_DESTROY(ll);
+ int r;
- sd_ipv4ll_detach_event(ll);
+ assert(acd);
+ assert(ll);
- free(ll->random_data);
- free(ll->random_data_state);
- free(ll);
+ switch (event) {
+ case IPV4ACD_EVENT_STOP:
+ ipv4ll_client_notify(ll, IPV4LL_EVENT_STOP);
- return NULL;
-}
+ ll->claimed_address = 0;
-DEFINE_TRIVIAL_CLEANUP_FUNC(sd_ipv4ll*, sd_ipv4ll_unref);
-#define _cleanup_ipv4ll_free_ _cleanup_(sd_ipv4ll_unrefp)
+ break;
+ case IPV4ACD_EVENT_BIND:
+ ll->claimed_address = ll->address;
+ ipv4ll_client_notify(ll, IPV4LL_EVENT_BIND);
-int sd_ipv4ll_new(sd_ipv4ll **ret) {
- _cleanup_ipv4ll_free_ sd_ipv4ll *ll = NULL;
+ break;
+ case IPV4ACD_EVENT_CONFLICT:
+ /* if an address was already bound we must call up to the
+ user to handle this, otherwise we just try again */
+ if (ll->claimed_address != 0) {
+ ipv4ll_client_notify(ll, IPV4LL_EVENT_CONFLICT);
- assert_return(ret, -EINVAL);
+ ll->claimed_address = 0;
+ } else {
+ r = ipv4ll_pick_address(ll);
+ if (r < 0)
+ goto error;
- ll = new0(sd_ipv4ll, 1);
- if (!ll)
- return -ENOMEM;
+ r = sd_ipv4acd_start(ll->acd);
+ if (r < 0)
+ goto error;
+ }
- ll->n_ref = 1;
- ll->state = IPV4LL_STATE_INIT;
- ll->index = -1;
- ll->fd = -1;
+ break;
+ default:
+ assert_not_reached("Invalid IPv4ACD event.");
+ }
- *ret = ll;
- ll = NULL;
+ return;
- return 0;
+error:
+ ipv4ll_client_notify(ll, IPV4LL_EVENT_STOP);
}
diff --git a/src/libsystemd-network/test-acd.c b/src/libsystemd-network/test-acd.c
new file mode 100644
index 0000000000..f1f14ee119
--- /dev/null
+++ b/src/libsystemd-network/test-acd.c
@@ -0,0 +1,117 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright (C) 2014 Tom Gundersen <teg@jklm.no>
+
+ systemd 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.
+
+ systemd 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 systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <stdlib.h>
+#include <errno.h>
+#include <unistd.h>
+
+#include <linux/veth.h>
+#include <net/if.h>
+
+#include "sd-event.h"
+#include "sd-netlink.h"
+#include "sd-ipv4acd.h"
+
+#include "util.h"
+#include "event-util.h"
+#include "netlink-util.h"
+#include "in-addr-util.h"
+
+static void acd_handler(sd_ipv4acd *acd, int event, void *userdata) {
+ assert_se(acd);
+
+ switch (event) {
+ case IPV4ACD_EVENT_BIND:
+ log_info("bound");
+ break;
+ case IPV4ACD_EVENT_CONFLICT:
+ log_info("conflict");
+ break;
+ case IPV4ACD_EVENT_STOP:
+ log_error("the client was stopped");
+ break;
+ default:
+ assert_not_reached("invalid ACD event");
+ }
+}
+
+static int client_run(int ifindex, const struct in_addr *pa, const struct ether_addr *ha, sd_event *e) {
+ sd_ipv4acd *acd;
+
+ assert_se(sd_ipv4acd_new(&acd) >= 0);
+ assert_se(sd_ipv4acd_attach_event(acd, e, 0) >= 0);
+
+ assert_se(sd_ipv4acd_set_index(acd, ifindex) >= 0);
+ assert_se(sd_ipv4acd_set_mac(acd, ha) >= 0);
+ assert_se(sd_ipv4acd_set_address(acd, pa) >= 0);
+ assert_se(sd_ipv4acd_set_callback(acd, acd_handler, NULL) >= 0);
+
+ log_info("starting IPv4ACD client");
+
+ assert_se(sd_ipv4acd_start(acd) >= 0);
+
+ assert_se(sd_event_loop(e) >= 0);
+
+ assert_se(!sd_ipv4acd_unref(acd));
+
+ return EXIT_SUCCESS;
+}
+
+static int test_acd(const char *ifname, const char *address) {
+ _cleanup_event_unref_ sd_event *e = NULL;
+ _cleanup_netlink_unref_ sd_netlink *rtnl = NULL;
+ _cleanup_netlink_message_unref_ sd_netlink_message *m = NULL, *reply = NULL;
+ union in_addr_union pa;
+ struct ether_addr ha;
+ int ifindex;
+
+ assert_se(in_addr_from_string(AF_INET, address, &pa) >= 0);
+
+ assert_se(sd_event_new(&e) >= 0);
+
+ assert_se(sd_netlink_open(&rtnl) >= 0);
+ assert_se(sd_netlink_attach_event(rtnl, e, 0) >= 0);
+
+ assert_se(sd_rtnl_message_new_link(rtnl, &m, RTM_GETLINK, 0) >= 0);
+ assert_se(sd_netlink_message_append_string(m, IFLA_IFNAME, ifname) >= 0);
+ assert_se(sd_netlink_call(rtnl, m, 0, &reply) >= 0);
+
+ assert_se(sd_rtnl_message_link_get_ifindex(reply, &ifindex) >= 0);
+ assert_se(sd_netlink_message_read_ether_addr(reply, IFLA_ADDRESS, &ha) >= 0);
+
+ client_run(ifindex, &pa.in, &ha, e);
+
+ return EXIT_SUCCESS;
+}
+
+int main(int argc, char *argv[]) {
+ log_set_max_level(LOG_DEBUG);
+ log_parse_environment();
+ log_open();
+
+ if (argc == 3)
+ return test_acd(argv[1], argv[2]);
+ else {
+ log_error("This program takes two arguments.\n"
+ "\t %s <ifname> <IPv4 address>", program_invocation_short_name);
+ return EXIT_FAILURE;
+ }
+}
diff --git a/src/libsystemd-network/test-ipv4ll-manual.c b/src/libsystemd-network/test-ipv4ll-manual.c
new file mode 100644
index 0000000000..ad664cba51
--- /dev/null
+++ b/src/libsystemd-network/test-ipv4ll-manual.c
@@ -0,0 +1,129 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright (C) 2014 Tom Gundersen <teg@jklm.no>
+
+ systemd 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.
+
+ systemd 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 systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <stdlib.h>
+#include <errno.h>
+#include <unistd.h>
+
+#include <linux/veth.h>
+#include <net/if.h>
+
+#include "sd-event.h"
+#include "sd-netlink.h"
+#include "sd-ipv4ll.h"
+
+#include "util.h"
+#include "event-util.h"
+#include "netlink-util.h"
+#include "in-addr-util.h"
+
+static void ll_handler(sd_ipv4ll *ll, int event, void *userdata) {
+ _cleanup_free_ char *address = NULL;
+ struct in_addr addr = {};
+
+ assert_se(ll);
+
+ if (sd_ipv4ll_get_address(ll, &addr) >= 0)
+ assert_se(in_addr_to_string(AF_INET, (const union in_addr_union*) &addr, &address) >= 0);
+
+ switch (event) {
+ case IPV4LL_EVENT_BIND:
+ log_info("bound %s", strna(address));
+ break;
+ case IPV4LL_EVENT_CONFLICT:
+ log_info("conflict on %s", strna(address));
+ break;
+ case IPV4LL_EVENT_STOP:
+ log_error("the client was stopped with address %s", strna(address));
+ break;
+ default:
+ assert_not_reached("invalid LL event");
+ }
+}
+
+static int client_run(int ifindex, const char *seed_str, const struct ether_addr *ha, sd_event *e) {
+ sd_ipv4ll *ll;
+
+ assert_se(sd_ipv4ll_new(&ll) >= 0);
+ assert_se(sd_ipv4ll_attach_event(ll, e, 0) >= 0);
+
+ assert_se(sd_ipv4ll_set_index(ll, ifindex) >= 0);
+ assert_se(sd_ipv4ll_set_mac(ll, ha) >= 0);
+ assert_se(sd_ipv4ll_set_callback(ll, ll_handler, NULL) >= 0);
+
+ if (seed_str) {
+ unsigned seed;
+
+ assert_se(safe_atou(seed_str, &seed) >= 0);
+
+ assert_se(sd_ipv4ll_set_address_seed(ll, seed) >= 0);
+ }
+
+ log_info("starting IPv4LL client");
+
+ assert_se(sd_ipv4ll_start(ll) >= 0);
+
+ assert_se(sd_event_loop(e) >= 0);
+
+ assert_se(!sd_ipv4ll_unref(ll));
+
+ return EXIT_SUCCESS;
+}
+
+static int test_ll(const char *ifname, const char *seed) {
+ _cleanup_event_unref_ sd_event *e = NULL;
+ _cleanup_netlink_unref_ sd_netlink *rtnl = NULL;
+ _cleanup_netlink_message_unref_ sd_netlink_message *m = NULL, *reply = NULL;
+ struct ether_addr ha;
+ int ifindex;
+
+ assert_se(sd_event_new(&e) >= 0);
+
+ assert_se(sd_netlink_open(&rtnl) >= 0);
+ assert_se(sd_netlink_attach_event(rtnl, e, 0) >= 0);
+
+ assert_se(sd_rtnl_message_new_link(rtnl, &m, RTM_GETLINK, 0) >= 0);
+ assert_se(sd_netlink_message_append_string(m, IFLA_IFNAME, ifname) >= 0);
+ assert_se(sd_netlink_call(rtnl, m, 0, &reply) >= 0);
+
+ assert_se(sd_rtnl_message_link_get_ifindex(reply, &ifindex) >= 0);
+ assert_se(sd_netlink_message_read_ether_addr(reply, IFLA_ADDRESS, &ha) >= 0);
+
+ client_run(ifindex, seed, &ha, e);
+
+ return EXIT_SUCCESS;
+}
+
+int main(int argc, char *argv[]) {
+ log_set_max_level(LOG_DEBUG);
+ log_parse_environment();
+ log_open();
+
+ if (argc == 2)
+ return test_ll(argv[1], NULL);
+ else if (argc == 3)
+ return test_ll(argv[1], argv[2]);
+ else {
+ log_error("This program takes one or two arguments.\n"
+ "\t %s <ifname> [<seed>]", program_invocation_short_name);
+ return EXIT_FAILURE;
+ }
+}
diff --git a/src/libsystemd-network/test-ipv4ll.c b/src/libsystemd-network/test-ipv4ll.c
index d60ee98b25..f0e26bda07 100644
--- a/src/libsystemd-network/test-ipv4ll.c
+++ b/src/libsystemd-network/test-ipv4ll.c
@@ -31,7 +31,7 @@
#include "event-util.h"
#include "sd-ipv4ll.h"
-#include "ipv4ll-internal.h"
+#include "arp-util.h"
static bool verbose = false;
static bool extended = false;
@@ -56,10 +56,10 @@ static void basic_request_handler(sd_ipv4ll *ll, int event, void *userdata) {
}
}
-int arp_network_send_raw_socket(int fd, const union sockaddr_union *link,
- const struct ether_arp *arp) {
+static int arp_network_send_raw_socket(int fd, int ifindex,
+ const struct ether_arp *arp) {
assert_se(arp);
- assert_se(link);
+ assert_se(ifindex > 0);
assert_se(fd >= 0);
if (send(fd, arp, sizeof(struct ether_arp), 0) < 0)
@@ -68,51 +68,35 @@ int arp_network_send_raw_socket(int fd, const union sockaddr_union *link,
return 0;
}
-int arp_network_bind_raw_socket(int index, union sockaddr_union *link) {
- if (socketpair(AF_UNIX, SOCK_DGRAM | SOCK_NONBLOCK, 0, test_fd) < 0)
- return -errno;
+int arp_send_probe(int fd, int ifindex,
+ be32_t pa, const struct ether_addr *ha) {
+ struct ether_arp ea = {};
- return test_fd[0];
-}
+ assert(fd >= 0);
+ assert(ifindex > 0);
+ assert(pa != 0);
+ assert(ha);
-static void test_arp_header(struct ether_arp *arp) {
- assert_se(arp);
- assert_se(arp->ea_hdr.ar_hrd == htons(ARPHRD_ETHER)); /* HTYPE */
- assert_se(arp->ea_hdr.ar_pro == htons(ETHERTYPE_IP)); /* PTYPE */
- assert_se(arp->ea_hdr.ar_hln == ETH_ALEN); /* HLEN */
- assert_se(arp->ea_hdr.ar_pln == sizeof arp->arp_spa); /* PLEN */
- assert_se(arp->ea_hdr.ar_op == htons(ARPOP_REQUEST)); /* REQUEST */
+ return arp_network_send_raw_socket(fd, ifindex, &ea);
}
-static void test_arp_probe(void) {
- struct ether_arp arp;
- struct ether_addr mac_addr = {
- .ether_addr_octet = {'A', 'B', 'C', '1', '2', '3'}};
- be32_t pa = 0x3030;
+int arp_send_announcement(int fd, int ifindex,
+ be32_t pa, const struct ether_addr *ha) {
+ struct ether_arp ea = {};
- if (verbose)
- printf("* %s\n", __FUNCTION__);
+ assert(fd >= 0);
+ assert(ifindex > 0);
+ assert(pa != 0);
+ assert(ha);
- arp_packet_probe(&arp, pa, &mac_addr);
- test_arp_header(&arp);
- assert_se(memcmp(arp.arp_sha, &mac_addr, ETH_ALEN) == 0);
- assert_se(memcmp(arp.arp_tpa, &pa, sizeof(pa)) == 0);
+ return arp_network_send_raw_socket(fd, ifindex, &ea);
}
-static void test_arp_announce(void) {
- struct ether_arp arp;
- struct ether_addr mac_addr = {
- .ether_addr_octet = {'A', 'B', 'C', '1', '2', '3'}};
- be32_t pa = 0x3131;
-
- if (verbose)
- printf("* %s\n", __FUNCTION__);
+int arp_network_bind_raw_socket(int index, be32_t address, const struct ether_addr *eth_mac) {
+ if (socketpair(AF_UNIX, SOCK_DGRAM | SOCK_NONBLOCK, 0, test_fd) < 0)
+ return -errno;
- arp_packet_announcement(&arp, pa, &mac_addr);
- test_arp_header(&arp);
- assert_se(memcmp(arp.arp_sha, &mac_addr, ETH_ALEN) == 0);
- assert_se(memcmp(arp.arp_tpa, &pa, sizeof(pa)) == 0);
- assert_se(memcmp(arp.arp_spa, &pa, sizeof(pa)) == 0);
+ return test_fd[0];
}
static void test_public_api_setters(sd_event *e) {
@@ -134,9 +118,8 @@ static void test_public_api_setters(sd_event *e) {
assert_se(sd_ipv4ll_set_callback(NULL, NULL, NULL) == -EINVAL);
assert_se(sd_ipv4ll_set_callback(ll, NULL, NULL) == 0);
- assert_se(sd_ipv4ll_set_address_seed(NULL, NULL) == -EINVAL);
- assert_se(sd_ipv4ll_set_address_seed(ll, NULL) == -EINVAL);
- assert_se(sd_ipv4ll_set_address_seed(ll, seed) == 0);
+ assert_se(sd_ipv4ll_set_address_seed(NULL, *(unsigned *) seed) == -EINVAL);
+ assert_se(sd_ipv4ll_set_address_seed(ll, *(unsigned *) seed) == 0);
assert_se(sd_ipv4ll_set_mac(NULL, NULL) == -EINVAL);
assert_se(sd_ipv4ll_set_mac(ll, NULL) == -EINVAL);
@@ -149,7 +132,7 @@ static void test_public_api_setters(sd_event *e) {
assert_se(sd_ipv4ll_set_index(ll, 99) == 0);
assert_se(sd_ipv4ll_ref(ll) == ll);
- assert_se(sd_ipv4ll_unref(ll) == ll);
+ assert_se(sd_ipv4ll_unref(ll) == NULL);
/* Cleanup */
assert_se(sd_ipv4ll_unref(ll) == NULL);
@@ -184,21 +167,20 @@ static void test_basic_request(sd_event *e) {
sd_event_run(e, (uint64_t) -1);
assert_se(sd_ipv4ll_start(ll) == -EBUSY);
+ assert_se(sd_ipv4ll_is_running(ll));
+
/* PROBE */
sd_event_run(e, (uint64_t) -1);
assert_se(read(test_fd[1], &arp, sizeof(struct ether_arp)) == sizeof(struct ether_arp));
- test_arp_header(&arp);
if (extended) {
/* PROBE */
sd_event_run(e, (uint64_t) -1);
assert_se(read(test_fd[1], &arp, sizeof(struct ether_arp)) == sizeof(struct ether_arp));
- test_arp_header(&arp);
/* PROBE */
sd_event_run(e, (uint64_t) -1);
assert_se(read(test_fd[1], &arp, sizeof(struct ether_arp)) == sizeof(struct ether_arp));
- test_arp_header(&arp);
sd_event_run(e, (uint64_t) -1);
assert_se(basic_request_handler_bind == 1);
@@ -215,11 +197,13 @@ static void test_basic_request(sd_event *e) {
int main(int argc, char *argv[]) {
_cleanup_event_unref_ sd_event *e = NULL;
+ log_set_max_level(LOG_DEBUG);
+ log_parse_environment();
+ log_open();
+
assert_se(sd_event_new(&e) >= 0);
test_public_api_setters(e);
- test_arp_probe();
- test_arp_announce();
test_basic_request(e);
return 0;
diff --git a/src/network/networkd-ipv4ll.c b/src/network/networkd-ipv4ll.c
index 0a27a30278..1c34f55b4b 100644
--- a/src/network/networkd-ipv4ll.c
+++ b/src/network/networkd-ipv4ll.c
@@ -195,10 +195,7 @@ static void ipv4ll_handler(sd_ipv4ll *ll, int event, void *userdata){
}
break;
default:
- if (event < 0)
- log_link_warning(link, "IPv4 link-local error: %s", strerror(-event));
- else
- log_link_warning(link, "IPv4 link-local unknown event: %d", event);
+ log_link_warning(link, "IPv4 link-local unknown event: %d", event);
break;
}
}
@@ -218,7 +215,9 @@ int ipv4ll_configure(Link *link) {
if (link->udev_device) {
r = net_get_unique_predictable_data(link->udev_device, seed);
if (r >= 0) {
- r = sd_ipv4ll_set_address_seed(link->ipv4ll, seed);
+ assert_cc(sizeof(unsigned) <= 8);
+
+ r = sd_ipv4ll_set_address_seed(link->ipv4ll, *(unsigned *)seed);
if (r < 0)
return r;
}
diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c
index d21ba9a566..e20fc1bbbb 100644
--- a/src/systemctl/systemctl.c
+++ b/src/systemctl/systemctl.c
@@ -3005,6 +3005,7 @@ static int prepare_firmware_setup(sd_bus *bus) {
}
static int start_special(sd_bus *bus, char **args) {
+ _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
enum action a;
int r;
@@ -3029,6 +3030,31 @@ static int start_special(sd_bus *bus, char **args) {
r = update_reboot_param_file(args[1]);
if (r < 0)
return r;
+ } else if (a == ACTION_EXIT && strv_length(args) > 1) {
+ /* If the exit code is not given on the command line, don't
+ * reset it to zero: just keep it as it might have been set
+ * previously. */
+ uint8_t code = 0;
+
+ r = safe_atou8(args[1], &code);
+ if (r < 0) {
+ log_error("Invalid exit code.");
+ return -EINVAL;
+ }
+
+ r = sd_bus_call_method(
+ bus,
+ "org.freedesktop.systemd1",
+ "/org/freedesktop/systemd1",
+ "org.freedesktop.systemd1.Manager",
+ "SetExitCode",
+ &error,
+ NULL,
+ "y", code);
+ if (r < 0) {
+ log_error("Failed to execute operation: %s", bus_error_message(&error, r));
+ return r;
+ }
}
if (arg_force >= 2 &&
@@ -3107,7 +3133,7 @@ static int check_unit_failed(sd_bus *bus, char **args) {
static int kill_unit(sd_bus *bus, char **args) {
_cleanup_strv_free_ char **names = NULL;
- char **name;
+ char *kill_who = NULL, **name;
int r, q;
assert(bus);
@@ -3118,6 +3144,10 @@ static int kill_unit(sd_bus *bus, char **args) {
if (!arg_kill_who)
arg_kill_who = "all";
+ /* --fail was specified */
+ if (streq(arg_job_mode, "fail"))
+ kill_who = strjoina(arg_kill_who, "-fail", NULL);
+
r = expand_names(bus, args + 1, NULL, &names);
if (r < 0)
log_error_errno(r, "Failed to expand names: %m");
@@ -3133,7 +3163,7 @@ static int kill_unit(sd_bus *bus, char **args) {
"KillUnit",
&error,
NULL,
- "ssi", *names, arg_kill_who, arg_signal);
+ "ssi", *names, kill_who ? kill_who : arg_kill_who, arg_signal);
if (q < 0) {
log_error("Failed to kill unit %s: %s", *names, bus_error_message(&error, q));
if (r == 0)
@@ -6220,7 +6250,7 @@ static void systemctl_help(void) {
" poweroff Shut down and power-off the system\n"
" reboot [ARG] Shut down and reboot the system\n"
" kexec Shut down and reboot the system with kexec\n"
- " exit Request user instance exit\n"
+ " exit [EXIT_CODE] Request user instance or container exit\n"
" switch-root ROOT [INIT] Change to a different root file system\n"
" suspend Suspend the system\n"
" hibernate Hibernate the system\n"
@@ -7207,7 +7237,7 @@ static int systemctl_main(sd_bus *bus, int argc, char *argv[], int bus_error) {
{ "default", EQUAL, 1, start_special },
{ "rescue", EQUAL, 1, start_special },
{ "emergency", EQUAL, 1, start_special },
- { "exit", EQUAL, 1, start_special },
+ { "exit", LESS, 2, start_special },
{ "reset-failed", MORE, 1, reset_failed },
{ "enable", MORE, 2, enable_unit, NOBUS },
{ "disable", MORE, 2, enable_unit, NOBUS },
diff --git a/src/systemd/sd-ipv4acd.h b/src/systemd/sd-ipv4acd.h
new file mode 100644
index 0000000000..8844ae848d
--- /dev/null
+++ b/src/systemd/sd-ipv4acd.h
@@ -0,0 +1,55 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#ifndef foosdipv4acdfoo
+#define foosdipv4acdfoo
+
+/***
+ This file is part of systemd.
+
+ Copyright (C) 2014 Axis Communications AB. All rights reserved.
+ Copyright (C) 2015 Tom Gundersen
+
+ systemd 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.
+
+ systemd 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 systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <stdbool.h>
+#include <netinet/in.h>
+#include <net/ethernet.h>
+
+#include "sd-event.h"
+
+enum {
+ IPV4ACD_EVENT_STOP = 0,
+ IPV4ACD_EVENT_BIND = 1,
+ IPV4ACD_EVENT_CONFLICT = 2,
+};
+
+typedef struct sd_ipv4acd sd_ipv4acd;
+typedef void (*sd_ipv4acd_cb_t)(sd_ipv4acd *ll, int event, void *userdata);
+
+int sd_ipv4acd_detach_event(sd_ipv4acd *ll);
+int sd_ipv4acd_attach_event(sd_ipv4acd *ll, sd_event *event, int priority);
+int sd_ipv4acd_get_address(sd_ipv4acd *ll, struct in_addr *address);
+int sd_ipv4acd_set_callback(sd_ipv4acd *ll, sd_ipv4acd_cb_t cb, void *userdata);
+int sd_ipv4acd_set_mac(sd_ipv4acd *ll, const struct ether_addr *addr);
+int sd_ipv4acd_set_index(sd_ipv4acd *ll, int interface_index);
+int sd_ipv4acd_set_address(sd_ipv4acd *ll, const struct in_addr *address);
+bool sd_ipv4acd_is_running(sd_ipv4acd *ll);
+int sd_ipv4acd_start(sd_ipv4acd *ll);
+int sd_ipv4acd_stop(sd_ipv4acd *ll);
+sd_ipv4acd *sd_ipv4acd_ref(sd_ipv4acd *ll);
+sd_ipv4acd *sd_ipv4acd_unref(sd_ipv4acd *ll);
+int sd_ipv4acd_new (sd_ipv4acd **ret);
+
+#endif
diff --git a/src/systemd/sd-ipv4ll.h b/src/systemd/sd-ipv4ll.h
index d017158154..9581e99d31 100644
--- a/src/systemd/sd-ipv4ll.h
+++ b/src/systemd/sd-ipv4ll.h
@@ -43,7 +43,7 @@ int sd_ipv4ll_get_address(sd_ipv4ll *ll, struct in_addr *address);
int sd_ipv4ll_set_callback(sd_ipv4ll *ll, sd_ipv4ll_cb_t cb, void *userdata);
int sd_ipv4ll_set_mac(sd_ipv4ll *ll, const struct ether_addr *addr);
int sd_ipv4ll_set_index(sd_ipv4ll *ll, int interface_index);
-int sd_ipv4ll_set_address_seed(sd_ipv4ll *ll, uint8_t seed[8]);
+int sd_ipv4ll_set_address_seed(sd_ipv4ll *ll, unsigned seed);
bool sd_ipv4ll_is_running(sd_ipv4ll *ll);
int sd_ipv4ll_start(sd_ipv4ll *ll);
int sd_ipv4ll_stop(sd_ipv4ll *ll);