diff options
author | Tom Gundersen <teg@jklm.no> | 2015-09-21 15:13:18 +0200 |
---|---|---|
committer | Tom Gundersen <teg@jklm.no> | 2015-09-21 15:13:18 +0200 |
commit | a1b7a5bbdd8da94e41b5e79be7a6e853ae910d2b (patch) | |
tree | f9041eb0474312b6795bd581224281c03135d6e0 /src | |
parent | 26ce77961868085c47156adaacb48913e1952da3 (diff) | |
parent | 7b713b81c27277f37420c121f2c9eeb10646ff5a (diff) |
Merge pull request #1288 from teg/ipv4acd-3
sd-ipv4acd: split out as separate library from sd-ipv4ll
Diffstat (limited to 'src')
-rw-r--r-- | src/libsystemd-network/arp-util.c | 153 | ||||
-rw-r--r-- | src/libsystemd-network/arp-util.h (renamed from src/libsystemd-network/ipv4ll-internal.h) | 14 | ||||
-rw-r--r-- | src/libsystemd-network/ipv4ll-network.c | 91 | ||||
-rw-r--r-- | src/libsystemd-network/ipv4ll-packet.c | 71 | ||||
-rw-r--r-- | src/libsystemd-network/sd-ipv4acd.c | 529 | ||||
-rw-r--r-- | src/libsystemd-network/sd-ipv4ll.c | 641 | ||||
-rw-r--r-- | src/libsystemd-network/test-acd.c | 117 | ||||
-rw-r--r-- | src/libsystemd-network/test-ipv4ll-manual.c | 129 | ||||
-rw-r--r-- | src/libsystemd-network/test-ipv4ll.c | 82 | ||||
-rw-r--r-- | src/network/networkd-ipv4ll.c | 9 | ||||
-rw-r--r-- | src/systemd/sd-ipv4acd.h | 55 | ||||
-rw-r--r-- | src/systemd/sd-ipv4ll.h | 2 |
12 files changed, 1188 insertions, 705 deletions
diff --git a/src/libsystemd-network/arp-util.c b/src/libsystemd-network/arp-util.c new file mode 100644 index 0000000000..2f5b9b3731 --- /dev/null +++ b/src/libsystemd-network/arp-util.c @@ -0,0 +1,153 @@ +/*** + This file is part of systemd. + + Copyright (C) 2014 Axis Communications AB. All rights reserved. + Copyright (C) 2015 Tom Gundersen + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see <http://www.gnu.org/licenses/>. +***/ + +#include <linux/filter.h> +#include <arpa/inet.h> + +#include "util.h" +#include "arp-util.h" + +int arp_network_bind_raw_socket(int ifindex, be32_t address, const struct ether_addr *eth_mac) { + struct sock_filter filter[] = { + BPF_STMT(BPF_LD + BPF_W + BPF_LEN, 0), /* A <- packet length */ + BPF_JUMP(BPF_JMP + BPF_JGE + BPF_K, sizeof(struct ether_arp), 1, 0), /* packet >= arp packet ? */ + BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */ + BPF_STMT(BPF_LD + BPF_H + BPF_ABS, offsetof(struct ether_arp, ea_hdr.ar_hrd)), /* A <- header */ + BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ARPHRD_ETHER, 1, 0), /* header == ethernet ? */ + BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */ + BPF_STMT(BPF_LD + BPF_H + BPF_ABS, offsetof(struct ether_arp, ea_hdr.ar_pro)), /* A <- protocol */ + BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ETHERTYPE_IP, 1, 0), /* protocol == IP ? */ + BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */ + BPF_STMT(BPF_LD + BPF_B + BPF_ABS, offsetof(struct ether_arp, ea_hdr.ar_hln)), /* A <- hardware address length */ + BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, sizeof(struct ether_addr), 1, 0), /* length == sizeof(ether_addr)? */ + BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */ + BPF_STMT(BPF_LD + BPF_B + BPF_ABS, offsetof(struct ether_arp, ea_hdr.ar_pln)), /* A <- protocol address length */ + BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, sizeof(struct in_addr), 1, 0), /* length == sizeof(in_addr) ? */ + BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */ + BPF_STMT(BPF_LD + BPF_H + BPF_ABS, offsetof(struct ether_arp, ea_hdr.ar_op)), /* A <- operation */ + BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ARPOP_REQUEST, 2, 0), /* protocol == request ? */ + BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ARPOP_REPLY, 1, 0), /* protocol == reply ? */ + BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */ + /* Sender Hardware Address must be different from our own */ + BPF_STMT(BPF_LD + BPF_IMM, htobe32(*((uint32_t *) eth_mac))), /* A <- 4 bytes of client's MAC */ + BPF_STMT(BPF_MISC + BPF_TAX, 0), /* X <- A */ + BPF_STMT(BPF_LD + BPF_W + BPF_ABS, offsetof(struct ether_arp, arp_sha)), /* A <- 4 bytes of SHA */ + BPF_STMT(BPF_ALU + BPF_XOR + BPF_X, 0), /* A xor X */ + BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0, 0, 6), /* A == 0 ? */ + BPF_STMT(BPF_LD + BPF_IMM, htobe16(*((uint16_t *) (((char *) eth_mac) + 4)))), /* A <- remainder of client's MAC */ + BPF_STMT(BPF_MISC + BPF_TAX, 0), /* X <- A */ + BPF_STMT(BPF_LD + BPF_H + BPF_ABS, offsetof(struct ether_arp, arp_sha) + 4), /* A <- remainder of SHA */ + BPF_STMT(BPF_ALU + BPF_XOR + BPF_X, 0), /* A xor X */ + BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0, 0, 1), /* A == 0 ? */ + BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */ + /* Sender Protocol Address or Target Protocol Address must be equal to the one we care about*/ + BPF_STMT(BPF_LD + BPF_IMM, htobe32(address)), /* A <- clients IP */ + BPF_STMT(BPF_MISC + BPF_TAX, 0), /* X <- A */ + BPF_STMT(BPF_LD + BPF_W + BPF_ABS, offsetof(struct ether_arp, arp_spa)), /* A <- SPA */ + BPF_STMT(BPF_ALU + BPF_XOR + BPF_X, 0), /* X xor A */ + BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0, 0, 1), /* A == 0 ? */ + BPF_STMT(BPF_RET + BPF_K, 65535), /* return all */ + BPF_STMT(BPF_LD + BPF_IMM, htobe32(address)), /* A <- clients IP */ + BPF_STMT(BPF_MISC + BPF_TAX, 0), /* X <- A */ + BPF_STMT(BPF_LD + BPF_W + BPF_ABS, offsetof(struct ether_arp, arp_tpa)), /* A <- TPA */ + BPF_STMT(BPF_ALU + BPF_XOR + BPF_X, 0), /* X xor A */ + BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0, 0, 1), /* A == 0 ? */ + BPF_STMT(BPF_RET + BPF_K, 65535), /* return all */ + BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */ + }; + struct sock_fprog fprog = { + .len = ELEMENTSOF(filter), + .filter = (struct sock_filter*) filter + }; + union sockaddr_union link = { + .ll.sll_family = AF_PACKET, + .ll.sll_protocol = htons(ETH_P_ARP), + .ll.sll_ifindex = ifindex, + .ll.sll_halen = ETH_ALEN, + .ll.sll_addr = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }, + }; + _cleanup_close_ int s = -1; + int r; + + assert(ifindex > 0); + + s = socket(PF_PACKET, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0); + if (s < 0) + return -errno; + + r = setsockopt(s, SOL_SOCKET, SO_ATTACH_FILTER, &fprog, sizeof(fprog)); + if (r < 0) + return -errno; + + r = bind(s, &link.sa, sizeof(link.ll)); + if (r < 0) + return -errno; + + r = s; + s = -1; + + return r; +} + +static int arp_send_packet(int fd, int ifindex, + be32_t pa, const struct ether_addr *ha, + bool announce) { + union sockaddr_union link = { + .ll.sll_family = AF_PACKET, + .ll.sll_protocol = htons(ETH_P_ARP), + .ll.sll_ifindex = ifindex, + .ll.sll_halen = ETH_ALEN, + .ll.sll_addr = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }, + }; + struct ether_arp arp = { + .ea_hdr.ar_hrd = htons(ARPHRD_ETHER), /* HTYPE */ + .ea_hdr.ar_pro = htons(ETHERTYPE_IP), /* PTYPE */ + .ea_hdr.ar_hln = ETH_ALEN, /* HLEN */ + .ea_hdr.ar_pln = sizeof(be32_t), /* PLEN */ + .ea_hdr.ar_op = htons(ARPOP_REQUEST), /* REQUEST */ + }; + int r; + + assert(fd >= 0); + assert(pa != 0); + assert(ha); + + memcpy(&arp.arp_sha, ha, ETH_ALEN); + memcpy(&arp.arp_tpa, &pa, sizeof(pa)); + + if (announce) + memcpy(&arp.arp_spa, &pa, sizeof(pa)); + + r = sendto(fd, &arp, sizeof(struct ether_arp), 0, &link.sa, sizeof(link.ll)); + if (r < 0) + return -errno; + + return 0; +} + +int arp_send_probe(int fd, int ifindex, + be32_t pa, const struct ether_addr *ha) { + return arp_send_packet(fd, ifindex, pa, ha, false); +} + +int arp_send_announcement(int fd, int ifindex, + be32_t pa, const struct ether_addr *ha) { + return arp_send_packet(fd, ifindex, pa, ha, true); +} diff --git a/src/libsystemd-network/ipv4ll-internal.h b/src/libsystemd-network/arp-util.h index ae0ce43985..44e5c893a7 100644 --- a/src/libsystemd-network/ipv4ll-internal.h +++ b/src/libsystemd-network/arp-util.h @@ -26,13 +26,9 @@ #include "sparse-endian.h" #include "socket-util.h" -int arp_network_bind_raw_socket(int index, union sockaddr_union *link); -int arp_network_send_raw_socket(int fd, const union sockaddr_union *link, - const struct ether_arp *arp); +int arp_network_bind_raw_socket(int index, be32_t address, const struct ether_addr *eth_mac); -void arp_packet_init(struct ether_arp *arp); -void arp_packet_probe(struct ether_arp *arp, be32_t pa, const struct ether_addr *ha); -void arp_packet_announcement(struct ether_arp *arp, be32_t pa, const struct ether_addr *ha); -int arp_packet_verify_headers(struct ether_arp *arp); - -#define log_ipv4ll(ll, fmt, ...) log_internal(LOG_DEBUG, 0, __FILE__, __LINE__, __func__, "IPv4LL: " fmt, ##__VA_ARGS__) +int arp_send_probe(int fd, int ifindex, + be32_t pa, const struct ether_addr *ha); +int arp_send_announcement(int fd, int ifindex, + be32_t pa, const struct ether_addr *ha); diff --git a/src/libsystemd-network/ipv4ll-network.c b/src/libsystemd-network/ipv4ll-network.c deleted file mode 100644 index 93ffed408f..0000000000 --- a/src/libsystemd-network/ipv4ll-network.c +++ /dev/null @@ -1,91 +0,0 @@ -/*** - This file is part of systemd. - - Copyright (C) 2014 Axis Communications AB. All rights reserved. - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see <http://www.gnu.org/licenses/>. -***/ - -#include <linux/filter.h> - -#include "util.h" -#include "ipv4ll-internal.h" - -int arp_network_send_raw_socket(int fd, const union sockaddr_union *link, - const struct ether_arp *arp) { - int r; - - assert(arp); - assert(link); - assert(fd >= 0); - - r = sendto(fd, arp, sizeof(struct ether_arp), 0, &link->sa, sizeof(link->ll)); - if (r < 0) - return -errno; - - return 0; -} - -int arp_network_bind_raw_socket(int ifindex, union sockaddr_union *link) { - - static const struct sock_filter filter[] = { - BPF_STMT(BPF_LD + BPF_W + BPF_LEN, 0), /* A <- packet length */ - BPF_JUMP(BPF_JMP + BPF_JGE + BPF_K, sizeof(struct ether_arp), 1, 0), /* packet >= arp packet ? */ - BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */ - BPF_STMT(BPF_LD + BPF_H + BPF_ABS, offsetof(struct ether_arp, ea_hdr.ar_hrd)), /* A <- header */ - BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ARPHRD_ETHER, 1, 0), /* header == ethernet ? */ - BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */ - BPF_STMT(BPF_LD + BPF_H + BPF_ABS, offsetof(struct ether_arp, ea_hdr.ar_pro)), /* A <- protocol */ - BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ETHERTYPE_IP, 1, 0), /* protocol == IP ? */ - BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */ - BPF_STMT(BPF_LD + BPF_H + BPF_ABS, offsetof(struct ether_arp, ea_hdr.ar_op)), /* A <- operation */ - BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ARPOP_REQUEST, 0, 1), /* protocol == request ? */ - BPF_STMT(BPF_RET + BPF_K, 65535), /* return all */ - BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ARPOP_REPLY, 0, 1), /* protocol == reply ? */ - BPF_STMT(BPF_RET + BPF_K, 65535), /* return all */ - BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */ - }; - struct sock_fprog fprog = { - .len = ELEMENTSOF(filter), - .filter = (struct sock_filter*) filter - }; - _cleanup_close_ int s = -1; - int r; - - assert(ifindex > 0); - assert(link); - - s = socket(PF_PACKET, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0); - if (s < 0) - return -errno; - - r = setsockopt(s, SOL_SOCKET, SO_ATTACH_FILTER, &fprog, sizeof(fprog)); - if (r < 0) - return -errno; - - link->ll.sll_family = AF_PACKET; - link->ll.sll_protocol = htons(ETH_P_ARP); - link->ll.sll_ifindex = ifindex; - link->ll.sll_halen = ETH_ALEN; - memset(link->ll.sll_addr, 0xff, ETH_ALEN); - - r = bind(s, &link->sa, sizeof(link->ll)); - if (r < 0) - return -errno; - - r = s; - s = -1; - - return r; -} diff --git a/src/libsystemd-network/ipv4ll-packet.c b/src/libsystemd-network/ipv4ll-packet.c deleted file mode 100644 index 2b6c73ab4b..0000000000 --- a/src/libsystemd-network/ipv4ll-packet.c +++ /dev/null @@ -1,71 +0,0 @@ -/*** - This file is part of systemd. - - Copyright (C) 2014 Axis Communications AB. All rights reserved. - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see <http://www.gnu.org/licenses/>. -***/ -#include <arpa/inet.h> - -#include "util.h" -#include "ipv4ll-internal.h" - -void arp_packet_init(struct ether_arp *arp) { - assert(arp); - - memzero(arp, sizeof(struct ether_arp)); - /* Header */ - arp->ea_hdr.ar_hrd = htons(ARPHRD_ETHER); /* HTYPE */ - arp->ea_hdr.ar_pro = htons(ETHERTYPE_IP); /* PTYPE */ - arp->ea_hdr.ar_hln = ETH_ALEN; /* HLEN */ - arp->ea_hdr.ar_pln = sizeof arp->arp_spa; /* PLEN */ - arp->ea_hdr.ar_op = htons(ARPOP_REQUEST); /* REQUEST */ -} - -void arp_packet_probe(struct ether_arp *arp, be32_t pa, const struct ether_addr *ha) { - assert(ha); - - arp_packet_init(arp); - memcpy(arp->arp_sha, ha, ETH_ALEN); - memcpy(arp->arp_tpa, &pa, sizeof(pa)); -} - -void arp_packet_announcement(struct ether_arp *arp, be32_t pa, const struct ether_addr *ha) { - assert(ha); - - arp_packet_init(arp); - memcpy(arp->arp_sha, ha, ETH_ALEN); - memcpy(arp->arp_tpa, &pa, sizeof(pa)); - memcpy(arp->arp_spa, &pa, sizeof(pa)); -} - -int arp_packet_verify_headers(struct ether_arp *arp) { - assert(arp); - - if (arp->ea_hdr.ar_hrd != htons(ARPHRD_ETHER)) { - log_ipv4ll(NULL, "ignoring packet: header is not ARPHRD_ETHER"); - return -EINVAL; - } - if (arp->ea_hdr.ar_pro != htons(ETHERTYPE_IP)) { - log_ipv4ll(NULL, "ignoring packet: protocol is not ETHERTYPE_IP"); - return -EINVAL; - } - if (arp->ea_hdr.ar_op != htons(ARPOP_REQUEST) && - arp->ea_hdr.ar_op != htons(ARPOP_REPLY)) { - log_ipv4ll(NULL, "ignoring packet: operation is not ARPOP_REQUEST or ARPOP_REPLY"); - return -EINVAL; - } - - return 0; -} diff --git a/src/libsystemd-network/sd-ipv4acd.c b/src/libsystemd-network/sd-ipv4acd.c new file mode 100644 index 0000000000..ee5d13c030 --- /dev/null +++ b/src/libsystemd-network/sd-ipv4acd.c @@ -0,0 +1,529 @@ +/*** + This file is part of systemd. + + Copyright (C) 2014 Axis Communications AB. All rights reserved. + Copyright (C) 2015 Tom Gundersen + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see <http://www.gnu.org/licenses/>. +***/ + +#include <arpa/inet.h> +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "event-util.h" +#include "in-addr-util.h" +#include "list.h" +#include "refcnt.h" +#include "random-util.h" +#include "siphash24.h" +#include "util.h" + +#include "arp-util.h" +#include "sd-ipv4acd.h" + +/* Constants from the RFC */ +#define PROBE_WAIT 1 +#define PROBE_NUM 3 +#define PROBE_MIN 1 +#define PROBE_MAX 2 +#define ANNOUNCE_WAIT 2 +#define ANNOUNCE_NUM 2 +#define ANNOUNCE_INTERVAL 2 +#define MAX_CONFLICTS 10 +#define RATE_LIMIT_INTERVAL 60 +#define DEFEND_INTERVAL 10 + +#define IPV4ACD_NETWORK 0xA9FE0000L +#define IPV4ACD_NETMASK 0xFFFF0000L + +#define log_ipv4acd_full(ll, level, error, fmt, ...) log_internal(level, error, __FILE__, __LINE__, __func__, "ACD: " fmt, ##__VA_ARGS__) + +#define log_ipv4acd_debug(ll, ...) log_ipv4acd_full(ll, LOG_DEBUG, 0, ##__VA_ARGS__) +#define log_ipv4acd_info(ll, ...) log_ipv4acd_full(ll, LOG_INFO, 0, ##__VA_ARGS__) +#define log_ipv4acd_notice(ll, ...) log_ipv4acd_full(ll, LOG_NOTICE, 0, ##__VA_ARGS__) +#define log_ipv4acd_warning(ll, ...) log_ipv4acd_full(ll, LOG_WARNING, 0, ##__VA_ARGS__) +#define log_ipv4acd_error(ll, ...) log_ipv4acd_full(ll, LOG_ERR, 0, ##__VA_ARGS__) + +#define log_ipv4acd_debug_errno(ll, error, ...) log_ipv4acd_full(ll, LOG_DEBUG, error, ##__VA_ARGS__) +#define log_ipv4acd_info_errno(ll, error, ...) log_ipv4acd_full(ll, LOG_INFO, error, ##__VA_ARGS__) +#define log_ipv4acd_notice_errno(ll, error, ...) log_ipv4acd_full(ll, LOG_NOTICE, error, ##__VA_ARGS__) +#define log_ipv4acd_warning_errno(ll, error, ...) log_ipv4acd_full(ll, LOG_WARNING, error, ##__VA_ARGS__) +#define log_ipv4acd_error_errno(ll, error, ...) log_ipv4acd_full(ll, LOG_ERR, error, ##__VA_ARGS__) + +typedef enum IPv4ACDState { + IPV4ACD_STATE_INIT, + IPV4ACD_STATE_WAITING_PROBE, + IPV4ACD_STATE_PROBING, + IPV4ACD_STATE_WAITING_ANNOUNCE, + IPV4ACD_STATE_ANNOUNCING, + IPV4ACD_STATE_RUNNING, + _IPV4ACD_STATE_MAX, + _IPV4ACD_STATE_INVALID = -1 +} IPv4ACDState; + +struct sd_ipv4acd { + RefCount n_ref; + + IPv4ACDState state; + int index; + int fd; + int iteration; + int conflict; + sd_event_source *receive_message; + sd_event_source *timer; + usec_t defend_window; + be32_t address; + /* External */ + struct ether_addr mac_addr; + sd_event *event; + int event_priority; + sd_ipv4acd_cb_t cb; + void* userdata; +}; + +sd_ipv4acd *sd_ipv4acd_ref(sd_ipv4acd *ll) { + if (ll) + assert_se(REFCNT_INC(ll->n_ref) >= 2); + + return ll; +} + +sd_ipv4acd *sd_ipv4acd_unref(sd_ipv4acd *ll) { + if (!ll || REFCNT_DEC(ll->n_ref) > 0) + return NULL; + + ll->receive_message = sd_event_source_unref(ll->receive_message); + ll->fd = safe_close(ll->fd); + + ll->timer = sd_event_source_unref(ll->timer); + + sd_ipv4acd_detach_event(ll); + + free(ll); + + return NULL; +} + +DEFINE_TRIVIAL_CLEANUP_FUNC(sd_ipv4acd*, sd_ipv4acd_unref); +#define _cleanup_ipv4acd_unref_ _cleanup_(sd_ipv4acd_unrefp) + +int sd_ipv4acd_new(sd_ipv4acd **ret) { + _cleanup_ipv4acd_unref_ sd_ipv4acd *ll = NULL; + + assert_return(ret, -EINVAL); + + ll = new0(sd_ipv4acd, 1); + if (!ll) + return -ENOMEM; + + ll->n_ref = REFCNT_INIT; + ll->state = IPV4ACD_STATE_INIT; + ll->index = -1; + ll->fd = -1; + + *ret = ll; + ll = NULL; + + return 0; +} + +static void ipv4acd_set_state(sd_ipv4acd *ll, IPv4ACDState st, bool reset_counter) { + + assert(ll); + assert(st < _IPV4ACD_STATE_MAX); + + if (st == ll->state && !reset_counter) + ll->iteration++; + else { + ll->state = st; + ll->iteration = 0; + } +} + +static void ipv4acd_client_notify(sd_ipv4acd *ll, int event) { + assert(ll); + + if (ll->cb) + ll->cb(ll, event, ll->userdata); +} + +static void ipv4acd_stop(sd_ipv4acd *ll) { + assert(ll); + + ll->receive_message = sd_event_source_unref(ll->receive_message); + ll->fd = safe_close(ll->fd); + + ll->timer = sd_event_source_unref(ll->timer); + + log_ipv4acd_debug(ll, "STOPPED"); + + ipv4acd_set_state (ll, IPV4ACD_STATE_INIT, true); +} + +int sd_ipv4acd_stop(sd_ipv4acd *ll) { + assert_return(ll, -EINVAL); + + ipv4acd_stop(ll); + + ipv4acd_client_notify(ll, IPV4ACD_EVENT_STOP); + + return 0; +} + +static int ipv4acd_on_timeout(sd_event_source *s, uint64_t usec, void *userdata); + +static int ipv4acd_set_next_wakeup(sd_ipv4acd *ll, int sec, int random_sec) { + _cleanup_event_source_unref_ sd_event_source *timer = NULL; + usec_t next_timeout; + usec_t time_now; + int r; + + assert(sec >= 0); + assert(random_sec >= 0); + assert(ll); + + next_timeout = sec * USEC_PER_SEC; + + if (random_sec) + next_timeout += random_u32() % (random_sec * USEC_PER_SEC); + + assert_se(sd_event_now(ll->event, clock_boottime_or_monotonic(), &time_now) >= 0); + + r = sd_event_add_time(ll->event, &timer, clock_boottime_or_monotonic(), + time_now + next_timeout, 0, ipv4acd_on_timeout, ll); + if (r < 0) + return r; + + r = sd_event_source_set_priority(timer, ll->event_priority); + if (r < 0) + return r; + + r = sd_event_source_set_description(timer, "ipv4acd-timer"); + if (r < 0) + return r; + + ll->timer = sd_event_source_unref(ll->timer); + ll->timer = timer; + timer = NULL; + + return 0; +} + +static bool ipv4acd_arp_conflict(sd_ipv4acd *ll, struct ether_arp *arp) { + assert(ll); + assert(arp); + + /* see the BPF */ + if (memcmp(arp->arp_spa, &ll->address, sizeof(ll->address)) == 0) + return true; + + /* the TPA matched instead of the SPA, this is not a conflict */ + return false; +} + +static int ipv4acd_on_timeout(sd_event_source *s, uint64_t usec, void *userdata) { + sd_ipv4acd *ll = userdata; + int r = 0; + + assert(ll); + + switch (ll->state) { + case IPV4ACD_STATE_INIT: + + ipv4acd_set_state(ll, IPV4ACD_STATE_WAITING_PROBE, true); + + if (ll->conflict >= MAX_CONFLICTS) { + log_ipv4acd_notice(ll, "Max conflicts reached, delaying by %us", RATE_LIMIT_INTERVAL); + r = ipv4acd_set_next_wakeup(ll, RATE_LIMIT_INTERVAL, PROBE_WAIT); + if (r < 0) + goto out; + + ll->conflict = 0; + } else { + r = ipv4acd_set_next_wakeup(ll, 0, PROBE_WAIT); + if (r < 0) + goto out; + } + + break; + case IPV4ACD_STATE_WAITING_PROBE: + case IPV4ACD_STATE_PROBING: + /* Send a probe */ + r = arp_send_probe(ll->fd, ll->index, ll->address, &ll->mac_addr); + if (r < 0) { + log_ipv4acd_error_errno(ll, r, "Failed to send ARP probe: %m"); + goto out; + } else { + _cleanup_free_ char *address = NULL; + union in_addr_union addr = { .in.s_addr = ll->address }; + + r = in_addr_to_string(AF_INET, &addr, &address); + if (r >= 0) + log_ipv4acd_debug(ll, "Probing %s", address); + } + + if (ll->iteration < PROBE_NUM - 2) { + ipv4acd_set_state(ll, IPV4ACD_STATE_PROBING, false); + + r = ipv4acd_set_next_wakeup(ll, PROBE_MIN, (PROBE_MAX-PROBE_MIN)); + if (r < 0) + goto out; + } else { + ipv4acd_set_state(ll, IPV4ACD_STATE_WAITING_ANNOUNCE, true); + + r = ipv4acd_set_next_wakeup(ll, ANNOUNCE_WAIT, 0); + if (r < 0) + goto out; + } + + break; + + case IPV4ACD_STATE_ANNOUNCING: + if (ll->iteration >= ANNOUNCE_NUM - 1) { + ipv4acd_set_state(ll, IPV4ACD_STATE_RUNNING, false); + + break; + } + case IPV4ACD_STATE_WAITING_ANNOUNCE: + /* Send announcement packet */ + r = arp_send_announcement(ll->fd, ll->index, ll->address, &ll->mac_addr); + if (r < 0) { + log_ipv4acd_error_errno(ll, r, "Failed to send ARP announcement: %m"); + goto out; + } else + log_ipv4acd_debug(ll, "ANNOUNCE"); + + ipv4acd_set_state(ll, IPV4ACD_STATE_ANNOUNCING, false); + + r = ipv4acd_set_next_wakeup(ll, ANNOUNCE_INTERVAL, 0); + if (r < 0) + goto out; + + if (ll->iteration == 0) { + ll->conflict = 0; + ipv4acd_client_notify(ll, IPV4ACD_EVENT_BIND); + } + + break; + default: + assert_not_reached("Invalid state."); + } + +out: + if (r < 0) + sd_ipv4acd_stop(ll); + + return 1; +} + +static void ipv4acd_on_conflict(sd_ipv4acd *ll) { + _cleanup_free_ char *address = NULL; + union in_addr_union addr = { .in.s_addr = ll->address }; + int r; + + assert(ll); + + ll->conflict++; + + r = in_addr_to_string(AF_INET, &addr, &address); + if (r >= 0) + log_ipv4acd_debug(ll, "Conflict on %s (%u)", address, ll->conflict); + + ipv4acd_stop(ll); + + ipv4acd_client_notify(ll, IPV4ACD_EVENT_CONFLICT); +} + +static int ipv4acd_on_packet(sd_event_source *s, int fd, + uint32_t revents, void *userdata) { + sd_ipv4acd *ll = userdata; + struct ether_arp packet; + int r; + + assert(ll); + assert(fd >= 0); + + r = read(fd, &packet, sizeof(struct ether_arp)); + if (r < (int) sizeof(struct ether_arp)) + goto out; + + switch (ll->state) { + case IPV4ACD_STATE_ANNOUNCING: + case IPV4ACD_STATE_RUNNING: + if (ipv4acd_arp_conflict(ll, &packet)) { + usec_t ts; + + assert_se(sd_event_now(ll->event, clock_boottime_or_monotonic(), &ts) >= 0); + + /* Defend address */ + if (ts > ll->defend_window) { + ll->defend_window = ts + DEFEND_INTERVAL * USEC_PER_SEC; + r = arp_send_announcement(ll->fd, ll->index, ll->address, &ll->mac_addr); + if (r < 0) { + log_ipv4acd_error_errno(ll, r, "Failed to send ARP announcement: %m"); + goto out; + } else + log_ipv4acd_debug(ll, "DEFEND"); + + } else + ipv4acd_on_conflict(ll); + } + + break; + case IPV4ACD_STATE_WAITING_PROBE: + case IPV4ACD_STATE_PROBING: + case IPV4ACD_STATE_WAITING_ANNOUNCE: + /* BPF ensures this packet indicates a conflict */ + ipv4acd_on_conflict(ll); + + break; + default: + assert_not_reached("Invalid state."); + } + +out: + if (r < 0) + sd_ipv4acd_stop(ll); + + return 1; +} + +int sd_ipv4acd_set_index(sd_ipv4acd *ll, int interface_index) { + assert_return(ll, -EINVAL); + assert_return(interface_index > 0, -EINVAL); + assert_return(ll->state == IPV4ACD_STATE_INIT, -EBUSY); + + ll->index = interface_index; + + return 0; +} + +int sd_ipv4acd_set_mac(sd_ipv4acd *ll, const struct ether_addr *addr) { + assert_return(ll, -EINVAL); + assert_return(addr, -EINVAL); + assert_return(ll->state == IPV4ACD_STATE_INIT, -EBUSY); + + memcpy(&ll->mac_addr, addr, ETH_ALEN); + + return 0; +} + +int sd_ipv4acd_detach_event(sd_ipv4acd *ll) { + assert_return(ll, -EINVAL); + + ll->event = sd_event_unref(ll->event); + + return 0; +} + +int sd_ipv4acd_attach_event(sd_ipv4acd *ll, sd_event *event, int priority) { + int r; + + assert_return(ll, -EINVAL); + assert_return(!ll->event, -EBUSY); + + if (event) + ll->event = sd_event_ref(event); + else { + r = sd_event_default(&ll->event); + if (r < 0) + return r; + } + + ll->event_priority = priority; + + return 0; +} + +int sd_ipv4acd_set_callback(sd_ipv4acd *ll, sd_ipv4acd_cb_t cb, void *userdata) { + assert_return(ll, -EINVAL); + + ll->cb = cb; + ll->userdata = userdata; + + return 0; +} + +int sd_ipv4acd_set_address(sd_ipv4acd *ll, const struct in_addr *address){ + assert_return(ll, -EINVAL); + assert_return(address, -EINVAL); + assert_return(ll->state == IPV4ACD_STATE_INIT, -EBUSY); + + ll->address = address->s_addr; + + return 0; +} + +bool sd_ipv4acd_is_running(sd_ipv4acd *ll) { + assert_return(ll, false); + + return ll->state != IPV4ACD_STATE_INIT; +} + +static bool ether_addr_is_nul(const struct ether_addr *addr) { + const struct ether_addr nul_addr = {}; + + assert(addr); + + return memcmp(addr, &nul_addr, sizeof(struct ether_addr)) == 0; +} + +#define HASH_KEY SD_ID128_MAKE(df,04,22,98,3f,ad,14,52,f9,87,2e,d1,9c,70,e2,f2) + +int sd_ipv4acd_start(sd_ipv4acd *ll) { + int r; + + assert_return(ll, -EINVAL); + assert_return(ll->event, -EINVAL); + assert_return(ll->index > 0, -EINVAL); + assert_return(ll->address != 0, -EINVAL); + assert_return(!ether_addr_is_nul(&ll->mac_addr), -EINVAL); + assert_return(ll->state == IPV4ACD_STATE_INIT, -EBUSY); + + ll->defend_window = 0; + + r = arp_network_bind_raw_socket(ll->index, ll->address, &ll->mac_addr); + if (r < 0) + goto out; + + ll->fd = safe_close(ll->fd); + ll->fd = r; + + r = sd_event_add_io(ll->event, &ll->receive_message, ll->fd, + EPOLLIN, ipv4acd_on_packet, ll); + if (r < 0) + goto out; + + r = sd_event_source_set_priority(ll->receive_message, ll->event_priority); + if (r < 0) + goto out; + + r = sd_event_source_set_description(ll->receive_message, "ipv4acd-receive-message"); + if (r < 0) + goto out; + + r = ipv4acd_set_next_wakeup(ll, 0, 0); + if (r < 0) + goto out; +out: + if (r < 0) { + ipv4acd_stop(ll); + return r; + } + + return 0; +} diff --git a/src/libsystemd-network/sd-ipv4ll.c b/src/libsystemd-network/sd-ipv4ll.c index 14b9444dab..f0230b919c 100644 --- a/src/libsystemd-network/sd-ipv4ll.c +++ b/src/libsystemd-network/sd-ipv4ll.c @@ -2,6 +2,7 @@ This file is part of systemd. Copyright (C) 2014 Axis Communications AB. All rights reserved. + Copyright (C) 2015 Tom Gundersen systemd is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by @@ -23,429 +24,153 @@ #include <stdio.h> #include <arpa/inet.h> -#include "util.h" -#include "siphash24.h" +#include "event-util.h" #include "list.h" #include "random-util.h" +#include "refcnt.h" +#include "siphash24.h" +#include "sparse-endian.h" +#include "util.h" -#include "ipv4ll-internal.h" +#include "sd-ipv4acd.h" #include "sd-ipv4ll.h" -/* Constants from the RFC */ -#define PROBE_WAIT 1 -#define PROBE_NUM 3 -#define PROBE_MIN 1 -#define PROBE_MAX 2 -#define ANNOUNCE_WAIT 2 -#define ANNOUNCE_NUM 2 -#define ANNOUNCE_INTERVAL 2 -#define MAX_CONFLICTS 10 -#define RATE_LIMIT_INTERVAL 60 -#define DEFEND_INTERVAL 10 - #define IPV4LL_NETWORK 0xA9FE0000L #define IPV4LL_NETMASK 0xFFFF0000L -typedef enum IPv4LLTrigger{ - IPV4LL_TRIGGER_NULL, - IPV4LL_TRIGGER_PACKET, - IPV4LL_TRIGGER_TIMEOUT, - _IPV4LL_TRIGGER_MAX, - _IPV4LL_TRIGGER_INVALID = -1 -} IPv4LLTrigger; - -typedef enum IPv4LLState { - IPV4LL_STATE_INIT, - IPV4LL_STATE_WAITING_PROBE, - IPV4LL_STATE_PROBING, - IPV4LL_STATE_WAITING_ANNOUNCE, - IPV4LL_STATE_ANNOUNCING, - IPV4LL_STATE_RUNNING, - IPV4LL_STATE_STOPPED, - _IPV4LL_STATE_MAX, - _IPV4LL_STATE_INVALID = -1 -} IPv4LLState; +#define IPV4LL_DONT_DESTROY(ll) \ + _cleanup_ipv4ll_unref_ _unused_ sd_ipv4ll *_dont_destroy_##ll = sd_ipv4ll_ref(ll) struct sd_ipv4ll { unsigned n_ref; - IPv4LLState state; - int index; - int fd; - union sockaddr_union link; - int iteration; - int conflict; - sd_event_source *receive_message; - sd_event_source *timer; - usec_t next_wakeup; - usec_t defend_window; - int next_wakeup_valid; - be32_t address; + sd_ipv4acd *acd; + be32_t address; /* the address pushed to ACD */ struct random_data *random_data; char *random_data_state; + /* External */ be32_t claimed_address; - struct ether_addr mac_addr; - sd_event *event; - int event_priority; sd_ipv4ll_cb_t cb; void* userdata; }; -static void ipv4ll_run_state_machine(sd_ipv4ll *ll, IPv4LLTrigger trigger, void *trigger_data); - -static void ipv4ll_set_state(sd_ipv4ll *ll, IPv4LLState st, int reset_counter) { - - assert(ll); - assert(st < _IPV4LL_STATE_MAX); - - if (st == ll->state && !reset_counter) { - ll->iteration++; - } else { - ll->state = st; - ll->iteration = 0; - } -} - -static sd_ipv4ll *ipv4ll_client_notify(sd_ipv4ll *ll, int event) { - assert(ll); - - if (ll->cb) { - ll = sd_ipv4ll_ref(ll); - ll->cb(ll, event, ll->userdata); - ll = sd_ipv4ll_unref(ll); - } - - return ll; -} - -static sd_ipv4ll *ipv4ll_stop(sd_ipv4ll *ll, int event) { - assert(ll); - - ll->receive_message = sd_event_source_unref(ll->receive_message); - ll->fd = safe_close(ll->fd); - - ll->timer = sd_event_source_unref(ll->timer); - - log_ipv4ll(ll, "STOPPED"); - - ll = ipv4ll_client_notify(ll, event); +sd_ipv4ll *sd_ipv4ll_ref(sd_ipv4ll *ll) { + if (!ll) + return NULL; - if (ll) { - ll->claimed_address = 0; - ipv4ll_set_state (ll, IPV4LL_STATE_INIT, 1); - } + assert(ll->n_ref >= 1); + ll->n_ref++; return ll; } -static int ipv4ll_pick_address(sd_ipv4ll *ll, be32_t *address) { - be32_t addr; - int r; - int32_t random; - - assert(ll); - assert(address); - assert(ll->random_data); - - do { - r = random_r(ll->random_data, &random); - if (r < 0) - return r; - addr = htonl((random & 0x0000FFFF) | IPV4LL_NETWORK); - } while (addr == ll->address || - (ntohl(addr) & IPV4LL_NETMASK) != IPV4LL_NETWORK || - (ntohl(addr) & 0x0000FF00) == 0x0000 || - (ntohl(addr) & 0x0000FF00) == 0xFF00); - - *address = addr; - return 0; -} - -static int ipv4ll_timer(sd_event_source *s, uint64_t usec, void *userdata) { - sd_ipv4ll *ll = (sd_ipv4ll*)userdata; - - assert(ll); - - ll->next_wakeup_valid = 0; - ipv4ll_run_state_machine(ll, IPV4LL_TRIGGER_TIMEOUT, NULL); - - return 0; -} - -static void ipv4ll_set_next_wakeup(sd_ipv4ll *ll, int sec, int random_sec) { - usec_t next_timeout = 0; - usec_t time_now = 0; - - assert(sec >= 0); - assert(random_sec >= 0); - assert(ll); - - next_timeout = sec * USEC_PER_SEC; - - if (random_sec) - next_timeout += random_u32() % (random_sec * USEC_PER_SEC); - - assert_se(sd_event_now(ll->event, clock_boottime_or_monotonic(), &time_now) >= 0); - - ll->next_wakeup = time_now + next_timeout; - ll->next_wakeup_valid = 1; -} - -static bool ipv4ll_arp_conflict (sd_ipv4ll *ll, struct ether_arp *arp) { - assert(ll); - assert(arp); - - if (memcmp(arp->arp_spa, &ll->address, sizeof(ll->address)) == 0 && - memcmp(arp->arp_sha, &ll->mac_addr, ETH_ALEN) != 0) - return true; +sd_ipv4ll *sd_ipv4ll_unref(sd_ipv4ll *ll) { + if (!ll) + return NULL; - return false; -} + assert(ll->n_ref >= 1); + ll->n_ref--; -static bool ipv4ll_arp_probe_conflict (sd_ipv4ll *ll, struct ether_arp *arp) { - assert(ll); - assert(arp); + if (ll->n_ref > 0) + return NULL; - if (ipv4ll_arp_conflict(ll, arp)) - return true; + sd_ipv4acd_unref(ll->acd); - if (memcmp(arp->arp_tpa, &ll->address, sizeof(ll->address)) == 0 && - memcmp(arp->arp_sha, &ll->mac_addr, ETH_ALEN)) - return true; + free(ll->random_data); + free(ll->random_data_state); + free(ll); - return false; + return NULL; } -static void ipv4ll_run_state_machine(sd_ipv4ll *ll, IPv4LLTrigger trigger, void *trigger_data) { - struct ether_arp out_packet; - int out_packet_ready = 0; - int r = 0; - - assert(ll); - assert(trigger < _IPV4LL_TRIGGER_MAX); - - if (ll->state == IPV4LL_STATE_INIT) { - - log_ipv4ll(ll, "PROBE"); - ipv4ll_set_state(ll, IPV4LL_STATE_WAITING_PROBE, 1); - ipv4ll_set_next_wakeup(ll, 0, PROBE_WAIT); - - } else if ((ll->state == IPV4LL_STATE_WAITING_PROBE && trigger == IPV4LL_TRIGGER_TIMEOUT) || - (ll->state == IPV4LL_STATE_PROBING && trigger == IPV4LL_TRIGGER_TIMEOUT && ll->iteration < PROBE_NUM-2)) { - - /* Send a probe */ - arp_packet_probe(&out_packet, ll->address, &ll->mac_addr); - out_packet_ready = 1; - ipv4ll_set_state(ll, IPV4LL_STATE_PROBING, 0); - - ipv4ll_set_next_wakeup(ll, PROBE_MIN, (PROBE_MAX-PROBE_MIN)); - - } else if (ll->state == IPV4LL_STATE_PROBING && trigger == IPV4LL_TRIGGER_TIMEOUT && ll->iteration >= PROBE_NUM-2) { - - /* Send the last probe */ - arp_packet_probe(&out_packet, ll->address, &ll->mac_addr); - out_packet_ready = 1; - ipv4ll_set_state(ll, IPV4LL_STATE_WAITING_ANNOUNCE, 1); - - ipv4ll_set_next_wakeup(ll, ANNOUNCE_WAIT, 0); - - } else if ((ll->state == IPV4LL_STATE_WAITING_ANNOUNCE && trigger == IPV4LL_TRIGGER_TIMEOUT) || - (ll->state == IPV4LL_STATE_ANNOUNCING && trigger == IPV4LL_TRIGGER_TIMEOUT && ll->iteration < ANNOUNCE_NUM-1)) { - - /* Send announcement packet */ - arp_packet_announcement(&out_packet, ll->address, &ll->mac_addr); - out_packet_ready = 1; - ipv4ll_set_state(ll, IPV4LL_STATE_ANNOUNCING, 0); - - ipv4ll_set_next_wakeup(ll, ANNOUNCE_INTERVAL, 0); - - if (ll->iteration == 0) { - log_ipv4ll(ll, "ANNOUNCE"); - ll->claimed_address = ll->address; - ll = ipv4ll_client_notify(ll, IPV4LL_EVENT_BIND); - if (!ll || ll->state == IPV4LL_STATE_STOPPED) - goto out; - - ll->conflict = 0; - } - - } else if ((ll->state == IPV4LL_STATE_ANNOUNCING && trigger == IPV4LL_TRIGGER_TIMEOUT && - ll->iteration >= ANNOUNCE_NUM-1)) { - - ipv4ll_set_state(ll, IPV4LL_STATE_RUNNING, 0); - ll->next_wakeup_valid = 0; - - } else if (trigger == IPV4LL_TRIGGER_PACKET) { - - int conflicted = 0; - usec_t time_now; - struct ether_arp* in_packet = (struct ether_arp*)trigger_data; - - assert(in_packet); - - if (IN_SET(ll->state, IPV4LL_STATE_ANNOUNCING, IPV4LL_STATE_RUNNING)) { - - if (ipv4ll_arp_conflict(ll, in_packet)) { - - r = sd_event_now(ll->event, clock_boottime_or_monotonic(), &time_now); - if (r < 0) - goto out; - - /* Defend address */ - if (time_now > ll->defend_window) { - ll->defend_window = time_now + DEFEND_INTERVAL * USEC_PER_SEC; - arp_packet_announcement(&out_packet, ll->address, &ll->mac_addr); - out_packet_ready = 1; - } else - conflicted = 1; - } - - } else if (IN_SET(ll->state, IPV4LL_STATE_WAITING_PROBE, - IPV4LL_STATE_PROBING, - IPV4LL_STATE_WAITING_ANNOUNCE)) { - - conflicted = ipv4ll_arp_probe_conflict(ll, in_packet); - } - - if (conflicted) { - log_ipv4ll(ll, "CONFLICT"); - ll = ipv4ll_client_notify(ll, IPV4LL_EVENT_CONFLICT); - if (!ll || ll->state == IPV4LL_STATE_STOPPED) - goto out; +DEFINE_TRIVIAL_CLEANUP_FUNC(sd_ipv4ll*, sd_ipv4ll_unref); +#define _cleanup_ipv4ll_unref_ _cleanup_(sd_ipv4ll_unrefp) - ll->claimed_address = 0; +static void ipv4ll_on_acd(sd_ipv4acd *ll, int event, void *userdata); - /* Pick a new address */ - r = ipv4ll_pick_address(ll, &ll->address); - if (r < 0) - goto out; - ll->conflict++; - ll->defend_window = 0; - ipv4ll_set_state(ll, IPV4LL_STATE_WAITING_PROBE, 1); +int sd_ipv4ll_new(sd_ipv4ll **ret) { + _cleanup_ipv4ll_unref_ sd_ipv4ll *ll = NULL; + int r; - if (ll->conflict >= MAX_CONFLICTS) { - log_ipv4ll(ll, "MAX_CONFLICTS"); - ipv4ll_set_next_wakeup(ll, RATE_LIMIT_INTERVAL, PROBE_WAIT); - } else - ipv4ll_set_next_wakeup(ll, 0, PROBE_WAIT); + assert_return(ret, -EINVAL); - } - } + ll = new0(sd_ipv4ll, 1); + if (!ll) + return -ENOMEM; - if (out_packet_ready) { - r = arp_network_send_raw_socket(ll->fd, &ll->link, &out_packet); - if (r < 0) { - log_ipv4ll(ll, "failed to send arp packet out"); - goto out; - } - } + r = sd_ipv4acd_new(&ll->acd); + if (r < 0) + return r; - if (ll->next_wakeup_valid) { - ll->timer = sd_event_source_unref(ll->timer); - r = sd_event_add_time(ll->event, &ll->timer, clock_boottime_or_monotonic(), - ll->next_wakeup, 0, ipv4ll_timer, ll); - if (r < 0) - goto out; + r = sd_ipv4acd_set_callback(ll->acd, ipv4ll_on_acd, ll); + if (r < 0) + return r; - r = sd_event_source_set_priority(ll->timer, ll->event_priority); - if (r < 0) - goto out; + ll->n_ref = 1; - r = sd_event_source_set_description(ll->timer, "ipv4ll-timer"); - if (r < 0) - goto out; - } + *ret = ll; + ll = NULL; -out: - if (r < 0 && ll) - ipv4ll_stop(ll, r); + return 0; } -static int ipv4ll_receive_message(sd_event_source *s, int fd, - uint32_t revents, void *userdata) { +int sd_ipv4ll_stop(sd_ipv4ll *ll) { int r; - struct ether_arp arp; - sd_ipv4ll *ll = (sd_ipv4ll*)userdata; - assert(ll); - - r = read(fd, &arp, sizeof(struct ether_arp)); - if (r < (int) sizeof(struct ether_arp)) - return 0; + assert_return(ll, -EINVAL); - r = arp_packet_verify_headers(&arp); + r = sd_ipv4acd_stop(ll->acd); if (r < 0) - return 0; - - ipv4ll_run_state_machine(ll, IPV4LL_TRIGGER_PACKET, &arp); + return r; return 0; } int sd_ipv4ll_set_index(sd_ipv4ll *ll, int interface_index) { assert_return(ll, -EINVAL); - assert_return(interface_index > 0, -EINVAL); - assert_return(IN_SET(ll->state, IPV4LL_STATE_INIT, - IPV4LL_STATE_STOPPED), -EBUSY); - ll->index = interface_index; - - return 0; + return sd_ipv4acd_set_index(ll->acd, interface_index); } +#define HASH_KEY SD_ID128_MAKE(df,04,22,98,3f,ad,14,52,f9,87,2e,d1,9c,70,e2,f2) + int sd_ipv4ll_set_mac(sd_ipv4ll *ll, const struct ether_addr *addr) { - bool need_restart = false; + int r; assert_return(ll, -EINVAL); - assert_return(addr, -EINVAL); - - if (memcmp(&ll->mac_addr, addr, ETH_ALEN) == 0) - return 0; - if (!IN_SET(ll->state, IPV4LL_STATE_INIT, IPV4LL_STATE_STOPPED)) { - log_ipv4ll(ll, "Changing MAC address on running IPv4LL " - "client, restarting"); - ll = ipv4ll_stop(ll, IPV4LL_EVENT_STOP); - need_restart = true; - } + if (!ll->random_data) { + uint8_t seed[8]; - if (!ll) - return 0; + /* If no random data is set, generate some from the MAC */ + siphash24(seed, &addr->ether_addr_octet, + ETH_ALEN, HASH_KEY.bytes); - memcpy(&ll->mac_addr, addr, ETH_ALEN); + assert_cc(sizeof(unsigned) <= 8); - if (need_restart) - sd_ipv4ll_start(ll); + r = sd_ipv4ll_set_address_seed(ll, *(unsigned*)seed); + if (r < 0) + return r; + } - return 0; + return sd_ipv4acd_set_mac(ll->acd, addr); } int sd_ipv4ll_detach_event(sd_ipv4ll *ll) { assert_return(ll, -EINVAL); - ll->event = sd_event_unref(ll->event); - - return 0; + return sd_ipv4acd_detach_event(ll->acd); } int sd_ipv4ll_attach_event(sd_ipv4ll *ll, sd_event *event, int priority) { int r; assert_return(ll, -EINVAL); - assert_return(!ll->event, -EBUSY); - - if (event) - ll->event = sd_event_ref(event); - else { - r = sd_event_default(&ll->event); - if (r < 0) { - ipv4ll_stop(ll, IPV4LL_EVENT_STOP); - return r; - } - } - ll->event_priority = priority; + r = sd_ipv4acd_attach_event(ll->acd, event, priority); + if (r < 0) + return r; return 0; } @@ -467,189 +192,147 @@ int sd_ipv4ll_get_address(sd_ipv4ll *ll, struct in_addr *address){ return -ENOENT; address->s_addr = ll->claimed_address; + return 0; } -int sd_ipv4ll_set_address_seed (sd_ipv4ll *ll, uint8_t seed[8]) { - unsigned int entropy; +int sd_ipv4ll_set_address_seed(sd_ipv4ll *ll, unsigned seed) { + _cleanup_free_ struct random_data *random_data = NULL; + _cleanup_free_ char *random_data_state = NULL; int r; assert_return(ll, -EINVAL); assert_return(seed, -EINVAL); - entropy = *seed; + random_data = new0(struct random_data, 1); + if (!random_data) + return -ENOMEM; - free(ll->random_data); - free(ll->random_data_state); + random_data_state = new0(char, 128); + if (!random_data_state) + return -ENOMEM; - ll->random_data = new0(struct random_data, 1); - ll->random_data_state = new0(char, 128); + r = initstate_r(seed, random_data_state, 128, random_data); + if (r < 0) + return r; - if (!ll->random_data || !ll->random_data_state) { - r = -ENOMEM; - goto error; - } + free(ll->random_data); + ll->random_data = random_data; + random_data = NULL; - r = initstate_r((unsigned int)entropy, ll->random_data_state, 128, ll->random_data); - if (r < 0) - goto error; + free(ll->random_data_state); + ll->random_data_state = random_data_state; + random_data_state = NULL; -error: - if (r < 0){ - free(ll->random_data); - free(ll->random_data_state); - ll->random_data = NULL; - ll->random_data_state = NULL; - } - return r; + return 0; } bool sd_ipv4ll_is_running(sd_ipv4ll *ll) { assert_return(ll, false); - return !IN_SET(ll->state, IPV4LL_STATE_INIT, IPV4LL_STATE_STOPPED); + return sd_ipv4acd_is_running(ll->acd); } -#define HASH_KEY SD_ID128_MAKE(df,04,22,98,3f,ad,14,52,f9,87,2e,d1,9c,70,e2,f2) - -int sd_ipv4ll_start (sd_ipv4ll *ll) { +static int ipv4ll_pick_address(sd_ipv4ll *ll) { + struct in_addr in_addr; + be32_t addr; int r; + int32_t random; - assert_return(ll, -EINVAL); - assert_return(ll->event, -EINVAL); - assert_return(ll->index > 0, -EINVAL); - assert_return(IN_SET(ll->state, IPV4LL_STATE_INIT, - IPV4LL_STATE_STOPPED), -EBUSY); + assert(ll); + assert(ll->random_data); - ll->state = IPV4LL_STATE_INIT; + do { + r = random_r(ll->random_data, &random); + if (r < 0) + return r; + addr = htonl((random & 0x0000FFFF) | IPV4LL_NETWORK); + } while (addr == ll->address || + (ntohl(addr) & IPV4LL_NETMASK) != IPV4LL_NETWORK || + (ntohl(addr) & 0x0000FF00) == 0x0000 || + (ntohl(addr) & 0x0000FF00) == 0xFF00); - r = arp_network_bind_raw_socket(ll->index, &ll->link); + in_addr.s_addr = addr; + r = sd_ipv4acd_set_address(ll->acd, &in_addr); if (r < 0) - goto out; + return r; - ll->fd = r; - ll->conflict = 0; - ll->defend_window = 0; - ll->claimed_address = 0; + ll->address = addr; - if (!ll->random_data) { - uint8_t seed[8]; + return 0; +} - /* Fallback to mac */ - siphash24(seed, &ll->mac_addr.ether_addr_octet, - ETH_ALEN, HASH_KEY.bytes); +int sd_ipv4ll_start(sd_ipv4ll *ll) { + int r; - r = sd_ipv4ll_set_address_seed(ll, seed); - if (r < 0) - goto out; - } + assert_return(ll, -EINVAL); + assert_return(ll->random_data, -EINVAL); if (ll->address == 0) { - r = ipv4ll_pick_address(ll, &ll->address); + r = ipv4ll_pick_address(ll); if (r < 0) - goto out; + return r; } - ipv4ll_set_state (ll, IPV4LL_STATE_INIT, 1); - - r = sd_event_add_io(ll->event, &ll->receive_message, ll->fd, - EPOLLIN, ipv4ll_receive_message, ll); + r = sd_ipv4acd_start(ll->acd); if (r < 0) - goto out; - - r = sd_event_source_set_priority(ll->receive_message, ll->event_priority); - if (r < 0) - goto out; - - r = sd_event_source_set_description(ll->receive_message, "ipv4ll-receive-message"); - if (r < 0) - goto out; - - r = sd_event_add_time(ll->event, - &ll->timer, - clock_boottime_or_monotonic(), - now(clock_boottime_or_monotonic()), 0, - ipv4ll_timer, ll); - - if (r < 0) - goto out; - - r = sd_event_source_set_priority(ll->timer, ll->event_priority); - if (r < 0) - goto out; - - r = sd_event_source_set_description(ll->timer, "ipv4ll-timer"); -out: - if (r < 0) - ipv4ll_stop(ll, IPV4LL_EVENT_STOP); + return r; return 0; } -int sd_ipv4ll_stop(sd_ipv4ll *ll) { - ipv4ll_stop(ll, IPV4LL_EVENT_STOP); - if (ll) - ipv4ll_set_state(ll, IPV4LL_STATE_STOPPED, 1); - - return 0; -} - -sd_ipv4ll *sd_ipv4ll_ref(sd_ipv4ll *ll) { - - if (!ll) - return NULL; - - assert(ll->n_ref >= 1); - ll->n_ref++; +static void ipv4ll_client_notify(sd_ipv4ll *ll, int event) { + assert(ll); - return ll; + if (ll->cb) + ll->cb(ll, event, ll->userdata); } -sd_ipv4ll *sd_ipv4ll_unref(sd_ipv4ll *ll) { - - if (!ll) - return NULL; - - assert(ll->n_ref >= 1); - ll->n_ref--; - - if (ll->n_ref > 0) - return ll; - - ll->receive_message = sd_event_source_unref(ll->receive_message); - ll->fd = safe_close(ll->fd); - - ll->timer = sd_event_source_unref(ll->timer); +void ipv4ll_on_acd(sd_ipv4acd *acd, int event, void *userdata) { + sd_ipv4ll *ll = userdata; + IPV4LL_DONT_DESTROY(ll); + int r; - sd_ipv4ll_detach_event(ll); + assert(acd); + assert(ll); - free(ll->random_data); - free(ll->random_data_state); - free(ll); + switch (event) { + case IPV4ACD_EVENT_STOP: + ipv4ll_client_notify(ll, IPV4LL_EVENT_STOP); - return NULL; -} + ll->claimed_address = 0; -DEFINE_TRIVIAL_CLEANUP_FUNC(sd_ipv4ll*, sd_ipv4ll_unref); -#define _cleanup_ipv4ll_free_ _cleanup_(sd_ipv4ll_unrefp) + break; + case IPV4ACD_EVENT_BIND: + ll->claimed_address = ll->address; + ipv4ll_client_notify(ll, IPV4LL_EVENT_BIND); -int sd_ipv4ll_new(sd_ipv4ll **ret) { - _cleanup_ipv4ll_free_ sd_ipv4ll *ll = NULL; + break; + case IPV4ACD_EVENT_CONFLICT: + /* if an address was already bound we must call up to the + user to handle this, otherwise we just try again */ + if (ll->claimed_address != 0) { + ipv4ll_client_notify(ll, IPV4LL_EVENT_CONFLICT); - assert_return(ret, -EINVAL); + ll->claimed_address = 0; + } else { + r = ipv4ll_pick_address(ll); + if (r < 0) + goto error; - ll = new0(sd_ipv4ll, 1); - if (!ll) - return -ENOMEM; + r = sd_ipv4acd_start(ll->acd); + if (r < 0) + goto error; + } - ll->n_ref = 1; - ll->state = IPV4LL_STATE_INIT; - ll->index = -1; - ll->fd = -1; + break; + default: + assert_not_reached("Invalid IPv4ACD event."); + } - *ret = ll; - ll = NULL; + return; - return 0; +error: + ipv4ll_client_notify(ll, IPV4LL_EVENT_STOP); } diff --git a/src/libsystemd-network/test-acd.c b/src/libsystemd-network/test-acd.c new file mode 100644 index 0000000000..f1f14ee119 --- /dev/null +++ b/src/libsystemd-network/test-acd.c @@ -0,0 +1,117 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright (C) 2014 Tom Gundersen <teg@jklm.no> + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see <http://www.gnu.org/licenses/>. +***/ + +#include <stdlib.h> +#include <errno.h> +#include <unistd.h> + +#include <linux/veth.h> +#include <net/if.h> + +#include "sd-event.h" +#include "sd-netlink.h" +#include "sd-ipv4acd.h" + +#include "util.h" +#include "event-util.h" +#include "netlink-util.h" +#include "in-addr-util.h" + +static void acd_handler(sd_ipv4acd *acd, int event, void *userdata) { + assert_se(acd); + + switch (event) { + case IPV4ACD_EVENT_BIND: + log_info("bound"); + break; + case IPV4ACD_EVENT_CONFLICT: + log_info("conflict"); + break; + case IPV4ACD_EVENT_STOP: + log_error("the client was stopped"); + break; + default: + assert_not_reached("invalid ACD event"); + } +} + +static int client_run(int ifindex, const struct in_addr *pa, const struct ether_addr *ha, sd_event *e) { + sd_ipv4acd *acd; + + assert_se(sd_ipv4acd_new(&acd) >= 0); + assert_se(sd_ipv4acd_attach_event(acd, e, 0) >= 0); + + assert_se(sd_ipv4acd_set_index(acd, ifindex) >= 0); + assert_se(sd_ipv4acd_set_mac(acd, ha) >= 0); + assert_se(sd_ipv4acd_set_address(acd, pa) >= 0); + assert_se(sd_ipv4acd_set_callback(acd, acd_handler, NULL) >= 0); + + log_info("starting IPv4ACD client"); + + assert_se(sd_ipv4acd_start(acd) >= 0); + + assert_se(sd_event_loop(e) >= 0); + + assert_se(!sd_ipv4acd_unref(acd)); + + return EXIT_SUCCESS; +} + +static int test_acd(const char *ifname, const char *address) { + _cleanup_event_unref_ sd_event *e = NULL; + _cleanup_netlink_unref_ sd_netlink *rtnl = NULL; + _cleanup_netlink_message_unref_ sd_netlink_message *m = NULL, *reply = NULL; + union in_addr_union pa; + struct ether_addr ha; + int ifindex; + + assert_se(in_addr_from_string(AF_INET, address, &pa) >= 0); + + assert_se(sd_event_new(&e) >= 0); + + assert_se(sd_netlink_open(&rtnl) >= 0); + assert_se(sd_netlink_attach_event(rtnl, e, 0) >= 0); + + assert_se(sd_rtnl_message_new_link(rtnl, &m, RTM_GETLINK, 0) >= 0); + assert_se(sd_netlink_message_append_string(m, IFLA_IFNAME, ifname) >= 0); + assert_se(sd_netlink_call(rtnl, m, 0, &reply) >= 0); + + assert_se(sd_rtnl_message_link_get_ifindex(reply, &ifindex) >= 0); + assert_se(sd_netlink_message_read_ether_addr(reply, IFLA_ADDRESS, &ha) >= 0); + + client_run(ifindex, &pa.in, &ha, e); + + return EXIT_SUCCESS; +} + +int main(int argc, char *argv[]) { + log_set_max_level(LOG_DEBUG); + log_parse_environment(); + log_open(); + + if (argc == 3) + return test_acd(argv[1], argv[2]); + else { + log_error("This program takes two arguments.\n" + "\t %s <ifname> <IPv4 address>", program_invocation_short_name); + return EXIT_FAILURE; + } +} diff --git a/src/libsystemd-network/test-ipv4ll-manual.c b/src/libsystemd-network/test-ipv4ll-manual.c new file mode 100644 index 0000000000..ad664cba51 --- /dev/null +++ b/src/libsystemd-network/test-ipv4ll-manual.c @@ -0,0 +1,129 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright (C) 2014 Tom Gundersen <teg@jklm.no> + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see <http://www.gnu.org/licenses/>. +***/ + +#include <stdlib.h> +#include <errno.h> +#include <unistd.h> + +#include <linux/veth.h> +#include <net/if.h> + +#include "sd-event.h" +#include "sd-netlink.h" +#include "sd-ipv4ll.h" + +#include "util.h" +#include "event-util.h" +#include "netlink-util.h" +#include "in-addr-util.h" + +static void ll_handler(sd_ipv4ll *ll, int event, void *userdata) { + _cleanup_free_ char *address = NULL; + struct in_addr addr = {}; + + assert_se(ll); + + if (sd_ipv4ll_get_address(ll, &addr) >= 0) + assert_se(in_addr_to_string(AF_INET, (const union in_addr_union*) &addr, &address) >= 0); + + switch (event) { + case IPV4LL_EVENT_BIND: + log_info("bound %s", strna(address)); + break; + case IPV4LL_EVENT_CONFLICT: + log_info("conflict on %s", strna(address)); + break; + case IPV4LL_EVENT_STOP: + log_error("the client was stopped with address %s", strna(address)); + break; + default: + assert_not_reached("invalid LL event"); + } +} + +static int client_run(int ifindex, const char *seed_str, const struct ether_addr *ha, sd_event *e) { + sd_ipv4ll *ll; + + assert_se(sd_ipv4ll_new(&ll) >= 0); + assert_se(sd_ipv4ll_attach_event(ll, e, 0) >= 0); + + assert_se(sd_ipv4ll_set_index(ll, ifindex) >= 0); + assert_se(sd_ipv4ll_set_mac(ll, ha) >= 0); + assert_se(sd_ipv4ll_set_callback(ll, ll_handler, NULL) >= 0); + + if (seed_str) { + unsigned seed; + + assert_se(safe_atou(seed_str, &seed) >= 0); + + assert_se(sd_ipv4ll_set_address_seed(ll, seed) >= 0); + } + + log_info("starting IPv4LL client"); + + assert_se(sd_ipv4ll_start(ll) >= 0); + + assert_se(sd_event_loop(e) >= 0); + + assert_se(!sd_ipv4ll_unref(ll)); + + return EXIT_SUCCESS; +} + +static int test_ll(const char *ifname, const char *seed) { + _cleanup_event_unref_ sd_event *e = NULL; + _cleanup_netlink_unref_ sd_netlink *rtnl = NULL; + _cleanup_netlink_message_unref_ sd_netlink_message *m = NULL, *reply = NULL; + struct ether_addr ha; + int ifindex; + + assert_se(sd_event_new(&e) >= 0); + + assert_se(sd_netlink_open(&rtnl) >= 0); + assert_se(sd_netlink_attach_event(rtnl, e, 0) >= 0); + + assert_se(sd_rtnl_message_new_link(rtnl, &m, RTM_GETLINK, 0) >= 0); + assert_se(sd_netlink_message_append_string(m, IFLA_IFNAME, ifname) >= 0); + assert_se(sd_netlink_call(rtnl, m, 0, &reply) >= 0); + + assert_se(sd_rtnl_message_link_get_ifindex(reply, &ifindex) >= 0); + assert_se(sd_netlink_message_read_ether_addr(reply, IFLA_ADDRESS, &ha) >= 0); + + client_run(ifindex, seed, &ha, e); + + return EXIT_SUCCESS; +} + +int main(int argc, char *argv[]) { + log_set_max_level(LOG_DEBUG); + log_parse_environment(); + log_open(); + + if (argc == 2) + return test_ll(argv[1], NULL); + else if (argc == 3) + return test_ll(argv[1], argv[2]); + else { + log_error("This program takes one or two arguments.\n" + "\t %s <ifname> [<seed>]", program_invocation_short_name); + return EXIT_FAILURE; + } +} diff --git a/src/libsystemd-network/test-ipv4ll.c b/src/libsystemd-network/test-ipv4ll.c index d60ee98b25..f0e26bda07 100644 --- a/src/libsystemd-network/test-ipv4ll.c +++ b/src/libsystemd-network/test-ipv4ll.c @@ -31,7 +31,7 @@ #include "event-util.h" #include "sd-ipv4ll.h" -#include "ipv4ll-internal.h" +#include "arp-util.h" static bool verbose = false; static bool extended = false; @@ -56,10 +56,10 @@ static void basic_request_handler(sd_ipv4ll *ll, int event, void *userdata) { } } -int arp_network_send_raw_socket(int fd, const union sockaddr_union *link, - const struct ether_arp *arp) { +static int arp_network_send_raw_socket(int fd, int ifindex, + const struct ether_arp *arp) { assert_se(arp); - assert_se(link); + assert_se(ifindex > 0); assert_se(fd >= 0); if (send(fd, arp, sizeof(struct ether_arp), 0) < 0) @@ -68,51 +68,35 @@ int arp_network_send_raw_socket(int fd, const union sockaddr_union *link, return 0; } -int arp_network_bind_raw_socket(int index, union sockaddr_union *link) { - if (socketpair(AF_UNIX, SOCK_DGRAM | SOCK_NONBLOCK, 0, test_fd) < 0) - return -errno; +int arp_send_probe(int fd, int ifindex, + be32_t pa, const struct ether_addr *ha) { + struct ether_arp ea = {}; - return test_fd[0]; -} + assert(fd >= 0); + assert(ifindex > 0); + assert(pa != 0); + assert(ha); -static void test_arp_header(struct ether_arp *arp) { - assert_se(arp); - assert_se(arp->ea_hdr.ar_hrd == htons(ARPHRD_ETHER)); /* HTYPE */ - assert_se(arp->ea_hdr.ar_pro == htons(ETHERTYPE_IP)); /* PTYPE */ - assert_se(arp->ea_hdr.ar_hln == ETH_ALEN); /* HLEN */ - assert_se(arp->ea_hdr.ar_pln == sizeof arp->arp_spa); /* PLEN */ - assert_se(arp->ea_hdr.ar_op == htons(ARPOP_REQUEST)); /* REQUEST */ + return arp_network_send_raw_socket(fd, ifindex, &ea); } -static void test_arp_probe(void) { - struct ether_arp arp; - struct ether_addr mac_addr = { - .ether_addr_octet = {'A', 'B', 'C', '1', '2', '3'}}; - be32_t pa = 0x3030; +int arp_send_announcement(int fd, int ifindex, + be32_t pa, const struct ether_addr *ha) { + struct ether_arp ea = {}; - if (verbose) - printf("* %s\n", __FUNCTION__); + assert(fd >= 0); + assert(ifindex > 0); + assert(pa != 0); + assert(ha); - arp_packet_probe(&arp, pa, &mac_addr); - test_arp_header(&arp); - assert_se(memcmp(arp.arp_sha, &mac_addr, ETH_ALEN) == 0); - assert_se(memcmp(arp.arp_tpa, &pa, sizeof(pa)) == 0); + return arp_network_send_raw_socket(fd, ifindex, &ea); } -static void test_arp_announce(void) { - struct ether_arp arp; - struct ether_addr mac_addr = { - .ether_addr_octet = {'A', 'B', 'C', '1', '2', '3'}}; - be32_t pa = 0x3131; - - if (verbose) - printf("* %s\n", __FUNCTION__); +int arp_network_bind_raw_socket(int index, be32_t address, const struct ether_addr *eth_mac) { + if (socketpair(AF_UNIX, SOCK_DGRAM | SOCK_NONBLOCK, 0, test_fd) < 0) + return -errno; - arp_packet_announcement(&arp, pa, &mac_addr); - test_arp_header(&arp); - assert_se(memcmp(arp.arp_sha, &mac_addr, ETH_ALEN) == 0); - assert_se(memcmp(arp.arp_tpa, &pa, sizeof(pa)) == 0); - assert_se(memcmp(arp.arp_spa, &pa, sizeof(pa)) == 0); + return test_fd[0]; } static void test_public_api_setters(sd_event *e) { @@ -134,9 +118,8 @@ static void test_public_api_setters(sd_event *e) { assert_se(sd_ipv4ll_set_callback(NULL, NULL, NULL) == -EINVAL); assert_se(sd_ipv4ll_set_callback(ll, NULL, NULL) == 0); - assert_se(sd_ipv4ll_set_address_seed(NULL, NULL) == -EINVAL); - assert_se(sd_ipv4ll_set_address_seed(ll, NULL) == -EINVAL); - assert_se(sd_ipv4ll_set_address_seed(ll, seed) == 0); + assert_se(sd_ipv4ll_set_address_seed(NULL, *(unsigned *) seed) == -EINVAL); + assert_se(sd_ipv4ll_set_address_seed(ll, *(unsigned *) seed) == 0); assert_se(sd_ipv4ll_set_mac(NULL, NULL) == -EINVAL); assert_se(sd_ipv4ll_set_mac(ll, NULL) == -EINVAL); @@ -149,7 +132,7 @@ static void test_public_api_setters(sd_event *e) { assert_se(sd_ipv4ll_set_index(ll, 99) == 0); assert_se(sd_ipv4ll_ref(ll) == ll); - assert_se(sd_ipv4ll_unref(ll) == ll); + assert_se(sd_ipv4ll_unref(ll) == NULL); /* Cleanup */ assert_se(sd_ipv4ll_unref(ll) == NULL); @@ -184,21 +167,20 @@ static void test_basic_request(sd_event *e) { sd_event_run(e, (uint64_t) -1); assert_se(sd_ipv4ll_start(ll) == -EBUSY); + assert_se(sd_ipv4ll_is_running(ll)); + /* PROBE */ sd_event_run(e, (uint64_t) -1); assert_se(read(test_fd[1], &arp, sizeof(struct ether_arp)) == sizeof(struct ether_arp)); - test_arp_header(&arp); if (extended) { /* PROBE */ sd_event_run(e, (uint64_t) -1); assert_se(read(test_fd[1], &arp, sizeof(struct ether_arp)) == sizeof(struct ether_arp)); - test_arp_header(&arp); /* PROBE */ sd_event_run(e, (uint64_t) -1); assert_se(read(test_fd[1], &arp, sizeof(struct ether_arp)) == sizeof(struct ether_arp)); - test_arp_header(&arp); sd_event_run(e, (uint64_t) -1); assert_se(basic_request_handler_bind == 1); @@ -215,11 +197,13 @@ static void test_basic_request(sd_event *e) { int main(int argc, char *argv[]) { _cleanup_event_unref_ sd_event *e = NULL; + log_set_max_level(LOG_DEBUG); + log_parse_environment(); + log_open(); + assert_se(sd_event_new(&e) >= 0); test_public_api_setters(e); - test_arp_probe(); - test_arp_announce(); test_basic_request(e); return 0; diff --git a/src/network/networkd-ipv4ll.c b/src/network/networkd-ipv4ll.c index 0a27a30278..1c34f55b4b 100644 --- a/src/network/networkd-ipv4ll.c +++ b/src/network/networkd-ipv4ll.c @@ -195,10 +195,7 @@ static void ipv4ll_handler(sd_ipv4ll *ll, int event, void *userdata){ } break; default: - if (event < 0) - log_link_warning(link, "IPv4 link-local error: %s", strerror(-event)); - else - log_link_warning(link, "IPv4 link-local unknown event: %d", event); + log_link_warning(link, "IPv4 link-local unknown event: %d", event); break; } } @@ -218,7 +215,9 @@ int ipv4ll_configure(Link *link) { if (link->udev_device) { r = net_get_unique_predictable_data(link->udev_device, seed); if (r >= 0) { - r = sd_ipv4ll_set_address_seed(link->ipv4ll, seed); + assert_cc(sizeof(unsigned) <= 8); + + r = sd_ipv4ll_set_address_seed(link->ipv4ll, *(unsigned *)seed); if (r < 0) return r; } diff --git a/src/systemd/sd-ipv4acd.h b/src/systemd/sd-ipv4acd.h new file mode 100644 index 0000000000..8844ae848d --- /dev/null +++ b/src/systemd/sd-ipv4acd.h @@ -0,0 +1,55 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +#ifndef foosdipv4acdfoo +#define foosdipv4acdfoo + +/*** + This file is part of systemd. + + Copyright (C) 2014 Axis Communications AB. All rights reserved. + Copyright (C) 2015 Tom Gundersen + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see <http://www.gnu.org/licenses/>. +***/ + +#include <stdbool.h> +#include <netinet/in.h> +#include <net/ethernet.h> + +#include "sd-event.h" + +enum { + IPV4ACD_EVENT_STOP = 0, + IPV4ACD_EVENT_BIND = 1, + IPV4ACD_EVENT_CONFLICT = 2, +}; + +typedef struct sd_ipv4acd sd_ipv4acd; +typedef void (*sd_ipv4acd_cb_t)(sd_ipv4acd *ll, int event, void *userdata); + +int sd_ipv4acd_detach_event(sd_ipv4acd *ll); +int sd_ipv4acd_attach_event(sd_ipv4acd *ll, sd_event *event, int priority); +int sd_ipv4acd_get_address(sd_ipv4acd *ll, struct in_addr *address); +int sd_ipv4acd_set_callback(sd_ipv4acd *ll, sd_ipv4acd_cb_t cb, void *userdata); +int sd_ipv4acd_set_mac(sd_ipv4acd *ll, const struct ether_addr *addr); +int sd_ipv4acd_set_index(sd_ipv4acd *ll, int interface_index); +int sd_ipv4acd_set_address(sd_ipv4acd *ll, const struct in_addr *address); +bool sd_ipv4acd_is_running(sd_ipv4acd *ll); +int sd_ipv4acd_start(sd_ipv4acd *ll); +int sd_ipv4acd_stop(sd_ipv4acd *ll); +sd_ipv4acd *sd_ipv4acd_ref(sd_ipv4acd *ll); +sd_ipv4acd *sd_ipv4acd_unref(sd_ipv4acd *ll); +int sd_ipv4acd_new (sd_ipv4acd **ret); + +#endif diff --git a/src/systemd/sd-ipv4ll.h b/src/systemd/sd-ipv4ll.h index d017158154..9581e99d31 100644 --- a/src/systemd/sd-ipv4ll.h +++ b/src/systemd/sd-ipv4ll.h @@ -43,7 +43,7 @@ int sd_ipv4ll_get_address(sd_ipv4ll *ll, struct in_addr *address); int sd_ipv4ll_set_callback(sd_ipv4ll *ll, sd_ipv4ll_cb_t cb, void *userdata); int sd_ipv4ll_set_mac(sd_ipv4ll *ll, const struct ether_addr *addr); int sd_ipv4ll_set_index(sd_ipv4ll *ll, int interface_index); -int sd_ipv4ll_set_address_seed(sd_ipv4ll *ll, uint8_t seed[8]); +int sd_ipv4ll_set_address_seed(sd_ipv4ll *ll, unsigned seed); bool sd_ipv4ll_is_running(sd_ipv4ll *ll); int sd_ipv4ll_start(sd_ipv4ll *ll); int sd_ipv4ll_stop(sd_ipv4ll *ll); |