diff options
author | Beniamino Galvani <bgalvani@redhat.com> | 2015-04-08 11:15:46 +0200 |
---|---|---|
committer | Beniamino Galvani <bgalvani@redhat.com> | 2015-04-08 16:12:44 +0200 |
commit | eab389bd8af0c8843bdcfaa3330fb6532e02574f (patch) | |
tree | 6d05b4c5d99bddf65a23a2a811452d6f357d35c9 | |
parent | 8af18182f3bf55270aadc83f32c518935d553a2a (diff) | |
download | NetworkManager-eab389bd8af0c8843bdcfaa3330fb6532e02574f.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.h | 38 | ||||
-rw-r--r-- | src/systemd/src/libsystemd-network/ipv4ll-network.c | 91 | ||||
-rw-r--r-- | src/systemd/src/libsystemd-network/ipv4ll-packet.c | 71 | ||||
-rw-r--r-- | src/systemd/src/libsystemd-network/sd-ipv4ll.c | 648 | ||||
-rw-r--r-- | src/systemd/src/shared/path-util.c | 89 | ||||
-rw-r--r-- | src/systemd/src/shared/path-util.h | 1 | ||||
-rw-r--r-- | src/systemd/src/shared/strv.c | 4 | ||||
-rw-r--r-- | src/systemd/src/shared/strv.h | 2 | ||||
-rw-r--r-- | src/systemd/src/shared/util.c | 515 | ||||
-rw-r--r-- | src/systemd/src/shared/util.h | 29 | ||||
-rw-r--r-- | src/systemd/src/systemd/sd-ipv4ll.h | 54 |
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 |