summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBeniamino Galvani <bgalvani@redhat.com>2015-04-08 11:15:46 +0200
committerBeniamino Galvani <bgalvani@redhat.com>2015-04-08 16:12:44 +0200
commiteab389bd8af0c8843bdcfaa3330fb6532e02574f (patch)
tree6d05b4c5d99bddf65a23a2a811452d6f357d35c9
parent8af18182f3bf55270aadc83f32c518935d553a2a (diff)
downloadNetworkManager-systemd-dhcp.tar.gz
build: update systemd codesystemd-dhcp
This is a direct dump from systemd git on 2015-04-08, git commit 431c3b6bab9c; it updates the existing files and imports systemd IPv4LL implementation. SYSTEMD_DIR=../systemd COMMIT=431c3b6bab9ceb54cd144d8df24d764a8a5f8fcc ( cd "$SYSTEMD_DIR" git checkout "$COMMIT" git reset --hard git clean -fdx ) /bin/cp "$SYSTEMD_DIR"/src/libsystemd/sd-id128/sd-id128.c ./src/systemd/src/libsystemd/sd-id128/sd-id128.c /bin/cp "$SYSTEMD_DIR"/src/libsystemd-network/dhcp-identifier.c ./src/systemd/src/libsystemd-network/dhcp-identifier.c /bin/cp "$SYSTEMD_DIR"/src/libsystemd-network/dhcp-identifier.h ./src/systemd/src/libsystemd-network/dhcp-identifier.h /bin/cp "$SYSTEMD_DIR"/src/libsystemd-network/dhcp-internal.h ./src/systemd/src/libsystemd-network/dhcp-internal.h /bin/cp "$SYSTEMD_DIR"/src/libsystemd-network/dhcp-lease-internal.h ./src/systemd/src/libsystemd-network/dhcp-lease-internal.h /bin/cp "$SYSTEMD_DIR"/src/libsystemd-network/dhcp-network.c ./src/systemd/src/libsystemd-network/dhcp-network.c /bin/cp "$SYSTEMD_DIR"/src/libsystemd-network/dhcp-option.c ./src/systemd/src/libsystemd-network/dhcp-option.c /bin/cp "$SYSTEMD_DIR"/src/libsystemd-network/dhcp-packet.c ./src/systemd/src/libsystemd-network/dhcp-packet.c /bin/cp "$SYSTEMD_DIR"/src/libsystemd-network/dhcp-protocol.h ./src/systemd/src/libsystemd-network/dhcp-protocol.h /bin/cp "$SYSTEMD_DIR"/src/libsystemd-network/dhcp6-internal.h ./src/systemd/src/libsystemd-network/dhcp6-internal.h /bin/cp "$SYSTEMD_DIR"/src/libsystemd-network/dhcp6-lease-internal.h ./src/systemd/src/libsystemd-network/dhcp6-lease-internal.h /bin/cp "$SYSTEMD_DIR"/src/libsystemd-network/dhcp6-network.c ./src/systemd/src/libsystemd-network/dhcp6-network.c /bin/cp "$SYSTEMD_DIR"/src/libsystemd-network/dhcp6-option.c ./src/systemd/src/libsystemd-network/dhcp6-option.c /bin/cp "$SYSTEMD_DIR"/src/libsystemd-network/dhcp6-protocol.h ./src/systemd/src/libsystemd-network/dhcp6-protocol.h /bin/cp "$SYSTEMD_DIR"/src/libsystemd-network/network-internal.c ./src/systemd/src/libsystemd-network/network-internal.c /bin/cp "$SYSTEMD_DIR"/src/libsystemd-network/network-internal.h ./src/systemd/src/libsystemd-network/network-internal.h /bin/cp "$SYSTEMD_DIR"/src/libsystemd-network/sd-dhcp-client.c ./src/systemd/src/libsystemd-network/sd-dhcp-client.c /bin/cp "$SYSTEMD_DIR"/src/libsystemd-network/sd-dhcp-lease.c ./src/systemd/src/libsystemd-network/sd-dhcp-lease.c /bin/cp "$SYSTEMD_DIR"/src/libsystemd-network/sd-dhcp6-client.c ./src/systemd/src/libsystemd-network/sd-dhcp6-client.c /bin/cp "$SYSTEMD_DIR"/src/libsystemd-network/sd-dhcp6-lease.c ./src/systemd/src/libsystemd-network/sd-dhcp6-lease.c /bin/cp "$SYSTEMD_DIR"/src/libsystemd-network/ipv4ll-internal.h ./src/systemd/src/libsystemd-network/ipv4ll-internal.h /bin/cp "$SYSTEMD_DIR"/src/libsystemd-network/ipv4ll-internal.h ./src/systemd/src/libsystemd-network/ipv4ll-internal.h /bin/cp "$SYSTEMD_DIR"/src/libsystemd-network/ipv4ll-network.c ./src/systemd/src/libsystemd-network/ipv4ll-network.c /bin/cp "$SYSTEMD_DIR"/src/libsystemd-network//ipv4ll-packet.c ./src/systemd/src/libsystemd-network//ipv4ll-packet.c /bin/cp "$SYSTEMD_DIR"/src/libsystemd-network/sd-ipv4ll.c ./src/systemd/src/libsystemd-network/sd-ipv4ll.c /bin/cp "$SYSTEMD_DIR"/src/shared/async.h ./src/systemd/src/shared/async.h /bin/cp "$SYSTEMD_DIR"/src/shared/fileio.c ./src/systemd/src/shared/fileio.c /bin/cp "$SYSTEMD_DIR"/src/shared/fileio.h ./src/systemd/src/shared/fileio.h /bin/cp "$SYSTEMD_DIR"/src/shared/list.h ./src/systemd/src/shared/list.h /bin/cp "$SYSTEMD_DIR"/src/shared/log.h ./src/systemd/src/shared/log.h /bin/cp "$SYSTEMD_DIR"/src/shared/macro.h ./src/systemd/src/shared/macro.h /bin/cp "$SYSTEMD_DIR"/src/shared/path-util.c ./src/systemd/src/shared/path-util.c /bin/cp "$SYSTEMD_DIR"/src/shared/path-util.h ./src/systemd/src/shared/path-util.h /bin/cp "$SYSTEMD_DIR"/src/shared/refcnt.h ./src/systemd/src/shared/refcnt.h /bin/cp "$SYSTEMD_DIR"/src/shared/siphash24.c ./src/systemd/src/shared/siphash24.c /bin/cp "$SYSTEMD_DIR"/src/shared/siphash24.h ./src/systemd/src/shared/siphash24.h /bin/cp "$SYSTEMD_DIR"/src/shared/socket-util.h ./src/systemd/src/shared/socket-util.h /bin/cp "$SYSTEMD_DIR"/src/shared/sparse-endian.h ./src/systemd/src/shared/sparse-endian.h /bin/cp "$SYSTEMD_DIR"/src/shared/strv.c ./src/systemd/src/shared/strv.c /bin/cp "$SYSTEMD_DIR"/src/shared/strv.h ./src/systemd/src/shared/strv.h /bin/cp "$SYSTEMD_DIR"/src/shared/time-util.c ./src/systemd/src/shared/time-util.c /bin/cp "$SYSTEMD_DIR"/src/shared/time-util.h ./src/systemd/src/shared/time-util.h /bin/cp "$SYSTEMD_DIR"/src/shared/utf8.c ./src/systemd/src/shared/utf8.c /bin/cp "$SYSTEMD_DIR"/src/shared/utf8.h ./src/systemd/src/shared/utf8.h /bin/cp "$SYSTEMD_DIR"/src/shared/util.c ./src/systemd/src/shared/util.c /bin/cp "$SYSTEMD_DIR"/src/shared/util.h ./src/systemd/src/shared/util.h /bin/cp "$SYSTEMD_DIR"/src/shared/unaligned.h ./src/systemd/src/shared/unaligned.h /bin/cp "$SYSTEMD_DIR"/src/shared/in-addr-util.c ./src/systemd/src/shared/in-addr-util.c /bin/cp "$SYSTEMD_DIR"/src/shared/in-addr-util.h ./src/systemd/src/shared/in-addr-util.h /bin/cp "$SYSTEMD_DIR"/src/systemd/_sd-common.h ./src/systemd/src/systemd/_sd-common.h /bin/cp "$SYSTEMD_DIR"/src/systemd/sd-dhcp-client.h ./src/systemd/src/systemd/sd-dhcp-client.h /bin/cp "$SYSTEMD_DIR"/src/systemd/sd-dhcp-lease.h ./src/systemd/src/systemd/sd-dhcp-lease.h /bin/cp "$SYSTEMD_DIR"/src/systemd/sd-dhcp6-client.h ./src/systemd/src/systemd/sd-dhcp6-client.h /bin/cp "$SYSTEMD_DIR"/src/systemd/sd-dhcp6-lease.h ./src/systemd/src/systemd/sd-dhcp6-lease.h /bin/cp "$SYSTEMD_DIR"/src/systemd/sd-event.h ./src/systemd/src/systemd/sd-event.h /bin/cp "$SYSTEMD_DIR"/src/systemd/sd-id128.h ./src/systemd/src/systemd/sd-id128.h /bin/cp "$SYSTEMD_DIR"/src/systemd/sd-ipv4ll.h ./src/systemd/src/systemd/sd-ipv4ll.h
-rw-r--r--src/systemd/src/libsystemd-network/ipv4ll-internal.h38
-rw-r--r--src/systemd/src/libsystemd-network/ipv4ll-network.c91
-rw-r--r--src/systemd/src/libsystemd-network/ipv4ll-packet.c71
-rw-r--r--src/systemd/src/libsystemd-network/sd-ipv4ll.c648
-rw-r--r--src/systemd/src/shared/path-util.c89
-rw-r--r--src/systemd/src/shared/path-util.h1
-rw-r--r--src/systemd/src/shared/strv.c4
-rw-r--r--src/systemd/src/shared/strv.h2
-rw-r--r--src/systemd/src/shared/util.c515
-rw-r--r--src/systemd/src/shared/util.h29
-rw-r--r--src/systemd/src/systemd/sd-ipv4ll.h54
11 files changed, 1172 insertions, 370 deletions
diff --git a/src/systemd/src/libsystemd-network/ipv4ll-internal.h b/src/systemd/src/libsystemd-network/ipv4ll-internal.h
new file mode 100644
index 0000000000..ae0ce43985
--- /dev/null
+++ b/src/systemd/src/libsystemd-network/ipv4ll-internal.h
@@ -0,0 +1,38 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+ 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 <netinet/if_ether.h>
+
+#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);
+
+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__)
diff --git a/src/systemd/src/libsystemd-network/ipv4ll-network.c b/src/systemd/src/libsystemd-network/ipv4ll-network.c
new file mode 100644
index 0000000000..93ffed408f
--- /dev/null
+++ b/src/systemd/src/libsystemd-network/ipv4ll-network.c
@@ -0,0 +1,91 @@
+/***
+ 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/systemd/src/libsystemd-network/ipv4ll-packet.c b/src/systemd/src/libsystemd-network/ipv4ll-packet.c
new file mode 100644
index 0000000000..2b6c73ab4b
--- /dev/null
+++ b/src/systemd/src/libsystemd-network/ipv4ll-packet.c
@@ -0,0 +1,71 @@
+/***
+ 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/systemd/src/libsystemd-network/sd-ipv4ll.c b/src/systemd/src/libsystemd-network/sd-ipv4ll.c
new file mode 100644
index 0000000000..02f2f9e0a9
--- /dev/null
+++ b/src/systemd/src/libsystemd-network/sd-ipv4ll.c
@@ -0,0 +1,648 @@
+/***
+ 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 <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#include <stdio.h>
+#include <arpa/inet.h>
+
+#include "util.h"
+#include "siphash24.h"
+#include "list.h"
+#include "refcnt.h"
+
+#include "ipv4ll-internal.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;
+
+struct sd_ipv4ll {
+ RefCount 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;
+ 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);
+
+ if (ll) {
+ ll->claimed_address = 0;
+ ipv4ll_set_state (ll, IPV4LL_STATE_INIT, 1);
+ }
+
+ 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);
+
+ if (sd_event_now(ll->event, clock_boottime_or_monotonic(), &time_now) < 0)
+ time_now = now(clock_boottime_or_monotonic());
+
+ 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;
+
+ return false;
+}
+
+static bool ipv4ll_arp_probe_conflict (sd_ipv4ll *ll, struct ether_arp *arp) {
+ assert(ll);
+ assert(arp);
+
+ if (ipv4ll_arp_conflict(ll, arp))
+ return true;
+
+ if (memcmp(arp->arp_tpa, &ll->address, sizeof(ll->address)) == 0 &&
+ memcmp(arp->arp_sha, &ll->mac_addr, ETH_ALEN))
+ return true;
+
+ return false;
+}
+
+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;
+
+ ll->claimed_address = 0;
+
+ /* 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);
+
+ 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);
+
+ }
+ }
+
+ 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;
+ }
+ }
+
+ 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_event_source_set_priority(ll->timer, ll->event_priority);
+ if (r < 0)
+ goto out;
+
+ r = sd_event_source_set_description(ll->timer, "ipv4ll-timer");
+ if (r < 0)
+ goto out;
+ }
+
+out:
+ if (r < 0 && ll)
+ ipv4ll_stop(ll, r);
+}
+
+static int ipv4ll_receive_message(sd_event_source *s, int fd,
+ uint32_t revents, void *userdata) {
+ 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;
+
+ r = arp_packet_verify_headers(&arp);
+ if (r < 0)
+ return 0;
+
+ ipv4ll_run_state_machine(ll, IPV4LL_TRIGGER_PACKET, &arp);
+
+ 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;
+}
+
+int sd_ipv4ll_set_mac(sd_ipv4ll *ll, const struct ether_addr *addr) {
+ bool need_restart = false;
+
+ 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)
+ return 0;
+
+ memcpy(&ll->mac_addr, addr, ETH_ALEN);
+
+ if (need_restart)
+ sd_ipv4ll_start(ll);
+
+ return 0;
+}
+
+int sd_ipv4ll_detach_event(sd_ipv4ll *ll) {
+ assert_return(ll, -EINVAL);
+
+ ll->event = sd_event_unref(ll->event);
+
+ return 0;
+}
+
+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;
+
+ return 0;
+}
+
+int sd_ipv4ll_set_callback(sd_ipv4ll *ll, sd_ipv4ll_cb_t cb, void *userdata) {
+ assert_return(ll, -EINVAL);
+
+ ll->cb = cb;
+ ll->userdata = userdata;
+
+ return 0;
+}
+
+int sd_ipv4ll_get_address(sd_ipv4ll *ll, struct in_addr *address){
+ assert_return(ll, -EINVAL);
+ assert_return(address, -EINVAL);
+
+ if (ll->claimed_address == 0) {
+ 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 r;
+
+ assert_return(ll, -EINVAL);
+ assert_return(seed, -EINVAL);
+
+ entropy = *seed;
+
+ free(ll->random_data);
+ free(ll->random_data_state);
+
+ ll->random_data = new0(struct random_data, 1);
+ ll->random_data_state = new0(char, 128);
+
+ if (!ll->random_data || !ll->random_data_state) {
+ r = -ENOMEM;
+ goto error;
+ }
+
+ r = initstate_r((unsigned int)entropy, ll->random_data_state, 128, ll->random_data);
+ if (r < 0)
+ goto error;
+
+error:
+ if (r < 0){
+ free(ll->random_data);
+ free(ll->random_data_state);
+ ll->random_data = NULL;
+ ll->random_data_state = NULL;
+ }
+ return r;
+}
+
+bool sd_ipv4ll_is_running(sd_ipv4ll *ll) {
+ assert_return(ll, -EINVAL);
+
+ return !IN_SET(ll->state, IPV4LL_STATE_INIT, IPV4LL_STATE_STOPPED);
+}
+
+#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) {
+ int r;
+
+ 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);
+
+ ll->state = IPV4LL_STATE_INIT;
+
+ r = arp_network_bind_raw_socket(ll->index, &ll->link);
+
+ if (r < 0)
+ goto out;
+
+ ll->fd = r;
+ ll->conflict = 0;
+ ll->defend_window = 0;
+ ll->claimed_address = 0;
+
+ if (!ll->random_data) {
+ uint8_t seed[8];
+
+ /* Fallback to mac */
+ siphash24(seed, &ll->mac_addr.ether_addr_octet,
+ ETH_ALEN, HASH_KEY.bytes);
+
+ r = sd_ipv4ll_set_address_seed(ll, seed);
+ if (r < 0)
+ goto out;
+ }
+
+ if (ll->address == 0) {
+ r = ipv4ll_pick_address(ll, &ll->address);
+ if (r < 0)
+ goto out;
+ }
+
+ 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);
+ 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 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)
+ assert_se(REFCNT_INC(ll->n_ref) >= 2);
+
+ return ll;
+}
+
+sd_ipv4ll *sd_ipv4ll_unref(sd_ipv4ll *ll) {
+ if (ll && REFCNT_DEC(ll->n_ref) == 0) {
+ 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_ipv4ll_detach_event(ll);
+
+ free(ll->random_data);
+ free(ll->random_data_state);
+ free(ll);
+
+ return NULL;
+ }
+
+ return ll;
+}
+
+DEFINE_TRIVIAL_CLEANUP_FUNC(sd_ipv4ll*, sd_ipv4ll_unref);
+#define _cleanup_ipv4ll_free_ _cleanup_(sd_ipv4ll_unrefp)
+
+int sd_ipv4ll_new(sd_ipv4ll **ret) {
+ _cleanup_ipv4ll_free_ sd_ipv4ll *ll = NULL;
+
+ assert_return(ret, -EINVAL);
+
+ ll = new0(sd_ipv4ll, 1);
+ if (!ll)
+ return -ENOMEM;
+
+ ll->n_ref = REFCNT_INIT;
+ ll->state = IPV4LL_STATE_INIT;
+ ll->index = -1;
+ ll->fd = -1;
+
+ *ret = ll;
+ ll = NULL;
+
+ return 0;
+}
diff --git a/src/systemd/src/shared/path-util.c b/src/systemd/src/shared/path-util.c
index 53c0079760..a01475a614 100644
--- a/src/systemd/src/shared/path-util.c
+++ b/src/systemd/src/shared/path-util.c
@@ -470,30 +470,19 @@ char* path_join(const char *root, const char *path, const char *rest) {
NULL);
}
-int path_is_mount_point(const char *t, bool allow_symlink) {
-
- union file_handle_union h = FILE_HANDLE_INIT;
+int fd_is_mount_point(int fd) {
+ union file_handle_union h = FILE_HANDLE_INIT, h_parent = FILE_HANDLE_INIT;
int mount_id = -1, mount_id_parent = -1;
+ bool nosupp = false;
struct stat a, b;
int r;
- _cleanup_close_ int fd = -1;
- bool nosupp = false;
+
+ assert(fd >= 0);
/* We are not actually interested in the file handles, but
* name_to_handle_at() also passes us the mount ID, hence use
* it but throw the handle away */
- if (path_equal(t, "/"))
- return 1;
-
- fd = openat(AT_FDCWD, t, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|(allow_symlink ? 0 : O_PATH));
- if (fd < 0) {
- if (errno == ENOENT)
- return 0;
-
- return -errno;
- }
-
r = name_to_handle_at(fd, "", &h.handle, &mount_id, AT_EMPTY_PATH);
if (r < 0) {
if (errno == ENOSYS)
@@ -502,53 +491,81 @@ int path_is_mount_point(const char *t, bool allow_symlink) {
goto fallback;
else if (errno == EOPNOTSUPP)
/* This kernel or file system does not support
- * name_to_handle_at(), hence fallback to the
+ * name_to_handle_at(), hence let's see if the
+ * upper fs supports it (in which case it is a
+ * mount point), otherwise fallback to the
* traditional stat() logic */
nosupp = true;
- else if (errno == ENOENT)
- return 0;
else
return -errno;
}
-
- h.handle.handle_bytes = MAX_HANDLE_SZ;
- r = name_to_handle_at(fd, "..", &h.handle, &mount_id_parent, 0);
- if (r < 0)
- if (errno == EOPNOTSUPP)
+ r = name_to_handle_at(fd, "..", &h_parent.handle, &mount_id_parent, 0);
+ if (r < 0) {
+ if (errno == EOPNOTSUPP) {
if (nosupp)
/* Neither parent nor child do name_to_handle_at()?
We have no choice but to fall back. */
goto fallback;
else
- /* The parent can't do name_to_handle_at() but
- * the directory we are interested in can?
- * Or the other way around?
+ /* The parent can't do name_to_handle_at() but the
+ * directory we are interested in can?
* If so, it must be a mount point. */
return 1;
- else
+ } else
return -errno;
- else
+ } else if (nosupp)
+ /* The parent can do name_to_handle_at() but the
+ * directory we are interested in can't? If so, it
+ * must be a mount point. */
+ return 1;
+ else {
+ /* If the file handle for the directory we are
+ * interested in and its parent are identical, we
+ * assume this is the root directory, which is a mount
+ * point. */
+
+ if (h.handle.handle_bytes == h_parent.handle.handle_bytes &&
+ h.handle.handle_type == h_parent.handle.handle_type &&
+ memcmp(h.handle.f_handle, h_parent.handle.f_handle, h.handle.handle_bytes) == 0)
+ return 1;
+
return mount_id != mount_id_parent;
+ }
fallback:
r = fstatat(fd, "", &a, AT_EMPTY_PATH);
-
- if (r < 0) {
- if (errno == ENOENT)
- return 0;
-
+ if (r < 0)
return -errno;
- }
-
r = fstatat(fd, "..", &b, 0);
if (r < 0)
return -errno;
+ /* A directory with same device and inode as its parent? Must
+ * be the root directory */
+ if (a.st_dev == b.st_dev &&
+ a.st_ino == b.st_ino)
+ return 1;
+
return a.st_dev != b.st_dev;
}
+int path_is_mount_point(const char *t, bool allow_symlink) {
+ _cleanup_close_ int fd = -1;
+
+ assert(t);
+
+ if (path_equal(t, "/"))
+ return 1;
+
+ fd = openat(AT_FDCWD, t, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|(allow_symlink ? 0 : O_PATH));
+ if (fd < 0)
+ return -errno;
+
+ return fd_is_mount_point(fd);
+}
+
int path_is_read_only_fs(const char *path) {
struct statvfs st;
diff --git a/src/systemd/src/shared/path-util.h b/src/systemd/src/shared/path-util.h
index ca81b49cbf..5548ce4a94 100644
--- a/src/systemd/src/shared/path-util.h
+++ b/src/systemd/src/shared/path-util.h
@@ -53,6 +53,7 @@ char** path_strv_make_absolute_cwd(char **l);
char** path_strv_resolve(char **l, const char *prefix);
char** path_strv_resolve_uniq(char **l, const char *prefix);
+int fd_is_mount_point(int fd);
int path_is_mount_point(const char *path, bool allow_symlink);
int path_is_read_only_fs(const char *path);
int path_is_os_tree(const char *path);
diff --git a/src/systemd/src/shared/strv.c b/src/systemd/src/shared/strv.c
index 8c6ba6a633..d44a72fc48 100644
--- a/src/systemd/src/shared/strv.c
+++ b/src/systemd/src/shared/strv.c
@@ -278,7 +278,7 @@ char **strv_split_newlines(const char *s) {
return l;
}
-int strv_split_quoted(char ***t, const char *s, bool relax) {
+int strv_split_quoted(char ***t, const char *s, UnquoteFlags flags) {
size_t n = 0, allocated = 0;
_cleanup_strv_free_ char **l = NULL;
int r;
@@ -289,7 +289,7 @@ int strv_split_quoted(char ***t, const char *s, bool relax) {
for (;;) {
_cleanup_free_ char *word = NULL;
- r = unquote_first_word(&s, &word, relax);
+ r = unquote_first_word(&s, &word, flags);
if (r < 0)
return r;
if (r == 0)
diff --git a/src/systemd/src/shared/strv.h b/src/systemd/src/shared/strv.h
index a80ccd6427..22f8f98fda 100644
--- a/src/systemd/src/shared/strv.h
+++ b/src/systemd/src/shared/strv.h
@@ -73,7 +73,7 @@ static inline bool strv_isempty(char * const *l) {
char **strv_split(const char *s, const char *separator);
char **strv_split_newlines(const char *s);
-int strv_split_quoted(char ***t, const char *s, bool relax);
+int strv_split_quoted(char ***t, const char *s, UnquoteFlags flags);
char *strv_join(char **l, const char *separator);
char *strv_join_quoted(char **l);
diff --git a/src/systemd/src/shared/util.c b/src/systemd/src/shared/util.c
index ad548da82a..de56d98feb 100644
--- a/src/systemd/src/shared/util.c
+++ b/src/systemd/src/shared/util.c
@@ -25,7 +25,6 @@
#include <stdlib.h>
#include <signal.h>
#include <libintl.h>
-#include <locale.h>
#include <stdio.h>
#include <syslog.h>
#include <sched.h>
@@ -1347,12 +1346,132 @@ char *cescape(const char *s) {
return r;
}
-char *cunescape_length_with_prefix(const char *s, size_t length, const char *prefix) {
+static int cunescape_one(const char *p, size_t length, char *ret) {
+ int r = 1;
+
+ assert(p);
+ assert(*p);
+ assert(ret);
+
+ if (length != (size_t) -1 && length < 1)
+ return -EINVAL;
+
+ switch (p[0]) {
+
+ case 'a':
+ *ret = '\a';
+ break;
+ case 'b':
+ *ret = '\b';
+ break;
+ case 'f':
+ *ret = '\f';
+ break;
+ case 'n':
+ *ret = '\n';
+ break;
+ case 'r':
+ *ret = '\r';
+ break;
+ case 't':
+ *ret = '\t';
+ break;
+ case 'v':
+ *ret = '\v';
+ break;
+ case '\\':
+ *ret = '\\';
+ break;
+ case '"':
+ *ret = '"';
+ break;
+ case '\'':
+ *ret = '\'';
+ break;
+
+ case 's':
+ /* This is an extension of the XDG syntax files */
+ *ret = ' ';
+ break;
+
+ case 'x': {
+ /* hexadecimal encoding */
+ int a, b;
+
+ if (length != (size_t) -1 && length < 3)
+ return -EINVAL;
+
+ a = unhexchar(p[1]);
+ if (a < 0)
+ return -EINVAL;
+
+ b = unhexchar(p[2]);
+ if (b < 0)
+ return -EINVAL;
+
+ /* don't allow NUL bytes */
+ if (a == 0 && b == 0)
+ return -EINVAL;
+
+ *ret = (char) ((a << 4) | b);
+ r = 3;
+ break;
+ }
+
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7': {
+ /* octal encoding */
+ int a, b, c, m;
+
+ if (length != (size_t) -1 && length < 4)
+ return -EINVAL;
+
+ a = unoctchar(p[0]);
+ if (a < 0)
+ return -EINVAL;
+
+ b = unoctchar(p[1]);
+ if (b < 0)
+ return -EINVAL;
+
+ c = unoctchar(p[2]);
+ if (c < 0)
+ return -EINVAL;
+
+ /* don't allow NUL bytes */
+ if (a == 0 && b == 0 && c == 0)
+ return -EINVAL;
+
+ /* Don't allow bytes above 255 */
+ m = (a << 6) | (b << 3) | c;
+ if (m > 255)
+ return -EINVAL;
+
+ *ret = (char) m;
+ r = 3;
+ break;
+ }
+
+ default:
+ return -EINVAL;
+ }
+
+ return r;
+}
+
+int cunescape_length_with_prefix(const char *s, size_t length, const char *prefix, UnescapeFlags flags, char **ret) {
char *r, *t;
const char *f;
size_t pl;
assert(s);
+ assert(ret);
/* Undoes C style string escaping, and optionally prefixes it. */
@@ -1360,135 +1479,61 @@ char *cunescape_length_with_prefix(const char *s, size_t length, const char *pre
r = new(char, pl+length+1);
if (!r)
- return NULL;
+ return -ENOMEM;
if (prefix)
memcpy(r, prefix, pl);
for (f = s, t = r + pl; f < s + length; f++) {
- size_t remaining = s + length - f;
+ size_t remaining;
+ int k;
+
+ remaining = s + length - f;
assert(remaining > 0);
- if (*f != '\\') { /* a literal literal */
+ if (*f != '\\') {
+ /* A literal literal, copy verbatim */
*(t++) = *f;
continue;
}
- if (--remaining == 0) { /* copy trailing backslash verbatim */
- *(t++) = *f;
- break;
- }
-
- f++;
-
- switch (*f) {
-
- case 'a':
- *(t++) = '\a';
- break;
- case 'b':
- *(t++) = '\b';
- break;
- case 'f':
- *(t++) = '\f';
- break;
- case 'n':
- *(t++) = '\n';
- break;
- case 'r':
- *(t++) = '\r';
- break;
- case 't':
- *(t++) = '\t';
- break;
- case 'v':
- *(t++) = '\v';
- break;
- case '\\':
- *(t++) = '\\';
- break;
- case '"':
- *(t++) = '"';
- break;
- case '\'':
- *(t++) = '\'';
- break;
-
- case 's':
- /* This is an extension of the XDG syntax files */
- *(t++) = ' ';
- break;
-
- case 'x': {
- /* hexadecimal encoding */
- int a = -1, b = -1;
-
- if (remaining >= 2) {
- a = unhexchar(f[1]);
- b = unhexchar(f[2]);
- }
-
- if (a < 0 || b < 0 || (a == 0 && b == 0)) {
- /* Invalid escape code, let's take it literal then */
- *(t++) = '\\';
- *(t++) = 'x';
- } else {
- *(t++) = (char) ((a << 4) | b);
- f += 2;
+ if (remaining == 1) {
+ if (flags & UNESCAPE_RELAX) {
+ /* A trailing backslash, copy verbatim */
+ *(t++) = *f;
+ continue;
}
- break;
+ return -EINVAL;
}
- case '0':
- case '1':
- case '2':
- case '3':
- case '4':
- case '5':
- case '6':
- case '7': {
- /* octal encoding */
- int a = -1, b = -1, c = -1;
-
- if (remaining >= 3) {
- a = unoctchar(f[0]);
- b = unoctchar(f[1]);
- c = unoctchar(f[2]);
- }
-
- if (a < 0 || b < 0 || c < 0 || (a == 0 && b == 0 && c == 0)) {
+ k = cunescape_one(f + 1, remaining - 1, t);
+ if (k < 0) {
+ if (flags & UNESCAPE_RELAX) {
/* Invalid escape code, let's take it literal then */
*(t++) = '\\';
- *(t++) = f[0];
- } else {
- *(t++) = (char) ((a << 6) | (b << 3) | c);
- f += 2;
+ continue;
}
- break;
+ return k;
}
- default:
- /* Invalid escape code, let's take it literal then */
- *(t++) = '\\';
- *(t++) = *f;
- break;
- }
+ f += k;
+ t++;
}
*t = 0;
- return r;
-}
-char *cunescape_length(const char *s, size_t length) {
- return cunescape_length_with_prefix(s, length, NULL);
+ *ret = r;
+ return t - r;
}
-char *cunescape(const char *s) {
- assert(s);
+int cunescape_length(const char *s, size_t length, UnescapeFlags flags, char **ret) {
+ return cunescape_length_with_prefix(s, length, NULL, flags, ret);
+}
- return cunescape_length(s, strlen(s));
+int cunescape(const char *s, UnescapeFlags flags, char **ret) {
+ return cunescape_length(s, strlen(s), flags, ret);
}
char *xescape(const char *s, const char *bad) {
@@ -2851,7 +2896,7 @@ int getttyname_malloc(int fd, char **ret) {
int getttyname_harder(int fd, char **r) {
int k;
- char *s;
+ char *s = NULL;
k = getttyname_malloc(fd, &s);
if (k < 0)
@@ -2957,101 +3002,14 @@ int get_ctty(pid_t pid, dev_t *_devnr, char **r) {
return 0;
}
-int rm_rf_children_dangerous(int fd, bool only_dirs, bool honour_sticky, struct stat *root_dev) {
- _cleanup_closedir_ DIR *d = NULL;
- int ret = 0;
-
- assert(fd >= 0);
-
- /* This returns the first error we run into, but nevertheless
- * tries to go on. This closes the passed fd. */
-
- d = fdopendir(fd);
- if (!d) {
- safe_close(fd);
-
- return errno == ENOENT ? 0 : -errno;
- }
-
- for (;;) {
- struct dirent *de;
- bool is_dir, keep_around;
- struct stat st;
- int r;
-
- errno = 0;
- de = readdir(d);
- if (!de) {
- if (errno != 0 && ret == 0)
- ret = -errno;
- return ret;
- }
-
- if (streq(de->d_name, ".") || streq(de->d_name, ".."))
- continue;
-
- if (de->d_type == DT_UNKNOWN ||
- honour_sticky ||
- (de->d_type == DT_DIR && root_dev)) {
- if (fstatat(fd, de->d_name, &st, AT_SYMLINK_NOFOLLOW) < 0) {
- if (ret == 0 && errno != ENOENT)
- ret = -errno;
- continue;
- }
-
- is_dir = S_ISDIR(st.st_mode);
- keep_around =
- honour_sticky &&
- (st.st_uid == 0 || st.st_uid == getuid()) &&
- (st.st_mode & S_ISVTX);
- } else {
- is_dir = de->d_type == DT_DIR;
- keep_around = false;
- }
-
- if (is_dir) {
- int subdir_fd;
-
- /* if root_dev is set, remove subdirectories only, if device is same as dir */
- if (root_dev && st.st_dev != root_dev->st_dev)
- continue;
-
- subdir_fd = openat(fd, de->d_name,
- O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW|O_NOATIME);
- if (subdir_fd < 0) {
- if (ret == 0 && errno != ENOENT)
- ret = -errno;
- continue;
- }
-
- r = rm_rf_children_dangerous(subdir_fd, only_dirs, honour_sticky, root_dev);
- if (r < 0 && ret == 0)
- ret = r;
-
- if (!keep_around)
- if (unlinkat(fd, de->d_name, AT_REMOVEDIR) < 0) {
- if (ret == 0 && errno != ENOENT)
- ret = -errno;
- }
-
- } else if (!only_dirs && !keep_around) {
-
- if (unlinkat(fd, de->d_name, 0) < 0) {
- if (ret == 0 && errno != ENOENT)
- ret = -errno;
- }
- }
- }
-}
-
-_pure_ static int is_temporary_fs(struct statfs *s) {
+bool is_temporary_fs(const struct statfs *s) {
assert(s);
return F_TYPE_EQUAL(s->f_type, TMPFS_MAGIC) ||
F_TYPE_EQUAL(s->f_type, RAMFS_MAGIC);
}
-int is_fd_on_temporary_fs(int fd) {
+int fd_is_temporary_fs(int fd) {
struct statfs s;
if (fstatfs(fd, &s) < 0)
@@ -3060,114 +3018,6 @@ int is_fd_on_temporary_fs(int fd) {
return is_temporary_fs(&s);
}
-int rm_rf_children(int fd, bool only_dirs, bool honour_sticky, struct stat *root_dev) {
- struct statfs s;
-
- assert(fd >= 0);
-
- if (fstatfs(fd, &s) < 0) {
- safe_close(fd);
- return -errno;
- }
-
- /* We refuse to clean disk file systems with this call. This
- * is extra paranoia just to be sure we never ever remove
- * non-state data */
- if (!is_temporary_fs(&s)) {
- log_error("Attempted to remove disk file system, and we can't allow that.");
- safe_close(fd);
- return -EPERM;
- }
-
- return rm_rf_children_dangerous(fd, only_dirs, honour_sticky, root_dev);
-}
-
-static int file_is_priv_sticky(const char *p) {
- struct stat st;
-
- assert(p);
-
- if (lstat(p, &st) < 0)
- return -errno;
-
- return
- (st.st_uid == 0 || st.st_uid == getuid()) &&
- (st.st_mode & S_ISVTX);
-}
-
-static int rm_rf_internal(const char *path, bool only_dirs, bool delete_root, bool honour_sticky, bool dangerous) {
- int fd, r;
- struct statfs s;
-
- assert(path);
-
- /* We refuse to clean the root file system with this
- * call. This is extra paranoia to never cause a really
- * seriously broken system. */
- if (path_equal(path, "/")) {
- log_error("Attempted to remove entire root file system, and we can't allow that.");
- return -EPERM;
- }
-
- fd = open(path, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW|O_NOATIME);
- if (fd < 0) {
-
- if (errno != ENOTDIR && errno != ELOOP)
- return -errno;
-
- if (!dangerous) {
- if (statfs(path, &s) < 0)
- return -errno;
-
- if (!is_temporary_fs(&s)) {
- log_error("Attempted to remove disk file system, and we can't allow that.");
- return -EPERM;
- }
- }
-
- if (delete_root && !only_dirs)
- if (unlink(path) < 0 && errno != ENOENT)
- return -errno;
-
- return 0;
- }
-
- if (!dangerous) {
- if (fstatfs(fd, &s) < 0) {
- safe_close(fd);
- return -errno;
- }
-
- if (!is_temporary_fs(&s)) {
- log_error("Attempted to remove disk file system, and we can't allow that.");
- safe_close(fd);
- return -EPERM;
- }
- }
-
- r = rm_rf_children_dangerous(fd, only_dirs, honour_sticky, NULL);
- if (delete_root) {
-
- if (honour_sticky && file_is_priv_sticky(path) > 0)
- return r;
-
- if (rmdir(path) < 0 && errno != ENOENT) {
- if (r == 0)
- r = -errno;
- }
- }
-
- return r;
-}
-
-int rm_rf(const char *path, bool only_dirs, bool delete_root, bool honour_sticky) {
- return rm_rf_internal(path, only_dirs, delete_root, honour_sticky, false);
-}
-
-int rm_rf_dangerous(const char *path, bool only_dirs, bool delete_root, bool honour_sticky) {
- return rm_rf_internal(path, only_dirs, delete_root, honour_sticky, true);
-}
-
int chmod_and_chown(const char *path, mode_t mode, uid_t uid, gid_t gid) {
assert(path);
@@ -3404,14 +3254,14 @@ char **replace_env_argv(char **argv, char **env) {
/* If $FOO appears as single word, replace it by the split up variable */
if ((*i)[0] == '$' && (*i)[1] != '{') {
char *e;
- char **w, **m;
+ char **w, **m = NULL;
unsigned q;
e = strv_env_get(env, *i+1);
if (e) {
int r;
- r = strv_split_quoted(&m, e, true);
+ r = strv_split_quoted(&m, e, UNQUOTE_RELAX);
if (r < 0) {
ret[k] = NULL;
strv_free(ret);
@@ -6382,7 +6232,7 @@ int parse_proc_cmdline(int (*parse_item)(const char *key, const char *value)) {
_cleanup_free_ char *word = NULL;
char *value = NULL;
- r = unquote_first_word(&p, &word, true);
+ r = unquote_first_word(&p, &word, UNQUOTE_RELAX);
if (r < 0)
return r;
if (r == 0)
@@ -6422,7 +6272,7 @@ int get_proc_cmdline_key(const char *key, char **value) {
_cleanup_free_ char *word = NULL;
const char *e;
- r = unquote_first_word(&p, &word, true);
+ r = unquote_first_word(&p, &word, UNQUOTE_RELAX);
if (r < 0)
return r;
if (r == 0)
@@ -6895,9 +6745,9 @@ int umount_recursive(const char *prefix, int flags) {
continue;
}
- p = cunescape(path);
- if (!p)
- return -ENOMEM;
+ r = cunescape(path, UNESCAPE_RELAX, &p);
+ if (r < 0)
+ return r;
if (!path_startswith(p, prefix))
continue;
@@ -6997,9 +6847,9 @@ int bind_remount_recursive(const char *prefix, bool ro) {
continue;
}
- p = cunescape(path);
- if (!p)
- return -ENOMEM;
+ r = cunescape(path, UNESCAPE_RELAX, &p);
+ if (r < 0)
+ return r;
/* Let's ignore autofs mounts. If they aren't
* triggered yet, we want to avoid triggering
@@ -7275,9 +7125,10 @@ int is_dir(const char* path, bool follow) {
return !!S_ISDIR(st.st_mode);
}
-int unquote_first_word(const char **p, char **ret, bool relax) {
+int unquote_first_word(const char **p, char **ret, UnquoteFlags flags) {
_cleanup_free_ char *s = NULL;
size_t allocated = 0, sz = 0;
+ int r;
enum {
START,
@@ -7335,7 +7186,7 @@ int unquote_first_word(const char **p, char **ret, bool relax) {
case VALUE_ESCAPE:
if (c == 0) {
- if (relax)
+ if (flags & UNQUOTE_RELAX)
goto finish;
return -EINVAL;
}
@@ -7343,6 +7194,14 @@ int unquote_first_word(const char **p, char **ret, bool relax) {
if (!GREEDY_REALLOC(s, allocated, sz+2))
return -ENOMEM;
+ if (flags & UNQUOTE_CUNESCAPE) {
+ r = cunescape_one(*p, (size_t) -1, &c);
+ if (r < 0)
+ return -EINVAL;
+
+ (*p) += r - 1;
+ }
+
s[sz++] = c;
state = VALUE;
@@ -7350,7 +7209,7 @@ int unquote_first_word(const char **p, char **ret, bool relax) {
case SINGLE_QUOTE:
if (c == 0) {
- if (relax)
+ if (flags & UNQUOTE_RELAX)
goto finish;
return -EINVAL;
} else if (c == '\'')
@@ -7368,7 +7227,7 @@ int unquote_first_word(const char **p, char **ret, bool relax) {
case SINGLE_QUOTE_ESCAPE:
if (c == 0) {
- if (relax)
+ if (flags & UNQUOTE_RELAX)
goto finish;
return -EINVAL;
}
@@ -7376,6 +7235,14 @@ int unquote_first_word(const char **p, char **ret, bool relax) {
if (!GREEDY_REALLOC(s, allocated, sz+2))
return -ENOMEM;
+ if (flags & UNQUOTE_CUNESCAPE) {
+ r = cunescape_one(*p, (size_t) -1, &c);
+ if (r < 0)
+ return -EINVAL;
+
+ (*p) += r - 1;
+ }
+
s[sz++] = c;
state = SINGLE_QUOTE;
break;
@@ -7398,7 +7265,7 @@ int unquote_first_word(const char **p, char **ret, bool relax) {
case DOUBLE_QUOTE_ESCAPE:
if (c == 0) {
- if (relax)
+ if (flags & UNQUOTE_RELAX)
goto finish;
return -EINVAL;
}
@@ -7406,6 +7273,14 @@ int unquote_first_word(const char **p, char **ret, bool relax) {
if (!GREEDY_REALLOC(s, allocated, sz+2))
return -ENOMEM;
+ if (flags & UNQUOTE_CUNESCAPE) {
+ r = cunescape_one(*p, (size_t) -1, &c);
+ if (r < 0)
+ return -EINVAL;
+
+ (*p) += r - 1;
+ }
+
s[sz++] = c;
state = DOUBLE_QUOTE;
break;
@@ -7435,7 +7310,7 @@ finish:
return 1;
}
-int unquote_many_words(const char **p, ...) {
+int unquote_many_words(const char **p, UnquoteFlags flags, ...) {
va_list ap;
char **l;
int n = 0, i, c, r;
@@ -7446,7 +7321,7 @@ int unquote_many_words(const char **p, ...) {
assert(p);
/* Count how many words are expected */
- va_start(ap, p);
+ va_start(ap, flags);
for (;;) {
if (!va_arg(ap, char **))
break;
@@ -7461,7 +7336,7 @@ int unquote_many_words(const char **p, ...) {
l = newa0(char*, n);
for (c = 0; c < n; c++) {
- r = unquote_first_word(p, &l[c], false);
+ r = unquote_first_word(p, &l[c], flags);
if (r < 0) {
int j;
@@ -7477,7 +7352,7 @@ int unquote_many_words(const char **p, ...) {
/* If we managed to parse all words, return them in the passed
* in parameters */
- va_start(ap, p);
+ va_start(ap, flags);
for (i = 0; i < n; i++) {
char **v;
diff --git a/src/systemd/src/shared/util.h b/src/systemd/src/shared/util.h
index 29e85bb7e1..e7a5d6366e 100644
--- a/src/systemd/src/shared/util.h
+++ b/src/systemd/src/shared/util.h
@@ -41,6 +41,7 @@
#include <locale.h>
#include <mntent.h>
#include <sys/inotify.h>
+#include <sys/statfs.h>
#if SIZEOF_PID_T == 4
# define PID_PRI PRIi32
@@ -310,9 +311,14 @@ char decchar(int x) _const_;
int undecchar(char c) _const_;
char *cescape(const char *s);
-char *cunescape(const char *s);
-char *cunescape_length(const char *s, size_t length);
-char *cunescape_length_with_prefix(const char *s, size_t length, const char *prefix);
+
+typedef enum UnescapeFlags {
+ UNESCAPE_RELAX = 1,
+} UnescapeFlags;
+
+int cunescape(const char *s, UnescapeFlags flags, char **ret);
+int cunescape_length(const char *s, size_t length, UnescapeFlags flags, char **ret);
+int cunescape_length_with_prefix(const char *s, size_t length, const char *prefix, UnescapeFlags flags, char **ret);
char *xescape(const char *s, const char *bad);
@@ -461,12 +467,8 @@ int get_ctty(pid_t, dev_t *_devnr, char **r);
int chmod_and_chown(const char *path, mode_t mode, uid_t uid, gid_t gid);
int fchmod_and_fchown(int fd, mode_t mode, uid_t uid, gid_t gid);
-int is_fd_on_temporary_fs(int fd);
-
-int rm_rf_children(int fd, bool only_dirs, bool honour_sticky, struct stat *root_dev);
-int rm_rf_children_dangerous(int fd, bool only_dirs, bool honour_sticky, struct stat *root_dev);
-int rm_rf(const char *path, bool only_dirs, bool delete_root, bool honour_sticky);
-int rm_rf_dangerous(const char *path, bool only_dirs, bool delete_root, bool honour_sticky);
+bool is_temporary_fs(const struct statfs *s) _pure_;
+int fd_is_temporary_fs(int fd);
int pipe_eof(int fd);
@@ -1017,8 +1019,13 @@ int take_password_lock(const char *root);
int is_symlink(const char *path);
int is_dir(const char *path, bool follow);
-int unquote_first_word(const char **p, char **ret, bool relax);
-int unquote_many_words(const char **p, ...) _sentinel_;
+typedef enum UnquoteFlags {
+ UNQUOTE_RELAX = 1,
+ UNQUOTE_CUNESCAPE = 2,
+} UnquoteFlags;
+
+int unquote_first_word(const char **p, char **ret, UnquoteFlags flags);
+int unquote_many_words(const char **p, UnquoteFlags flags, ...) _sentinel_;
int free_and_strdup(char **p, const char *s);
diff --git a/src/systemd/src/systemd/sd-ipv4ll.h b/src/systemd/src/systemd/sd-ipv4ll.h
new file mode 100644
index 0000000000..d017158154
--- /dev/null
+++ b/src/systemd/src/systemd/sd-ipv4ll.h
@@ -0,0 +1,54 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#ifndef foosdipv4llfoo
+#define foosdipv4llfoo
+
+/***
+ 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 <stdbool.h>
+#include <netinet/in.h>
+#include <net/ethernet.h>
+
+#include "sd-event.h"
+
+enum {
+ IPV4LL_EVENT_STOP = 0,
+ IPV4LL_EVENT_BIND = 1,
+ IPV4LL_EVENT_CONFLICT = 2,
+};
+
+typedef struct sd_ipv4ll sd_ipv4ll;
+typedef void (*sd_ipv4ll_cb_t)(sd_ipv4ll *ll, int event, void *userdata);
+
+int sd_ipv4ll_detach_event(sd_ipv4ll *ll);
+int sd_ipv4ll_attach_event(sd_ipv4ll *ll, sd_event *event, int priority);
+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]);
+bool sd_ipv4ll_is_running(sd_ipv4ll *ll);
+int sd_ipv4ll_start(sd_ipv4ll *ll);
+int sd_ipv4ll_stop(sd_ipv4ll *ll);
+sd_ipv4ll *sd_ipv4ll_ref(sd_ipv4ll *ll);
+sd_ipv4ll *sd_ipv4ll_unref(sd_ipv4ll *ll);
+int sd_ipv4ll_new (sd_ipv4ll **ret);
+
+#endif