summaryrefslogtreecommitdiff
path: root/src/libsystemd-network
diff options
context:
space:
mode:
Diffstat (limited to 'src/libsystemd-network')
-rw-r--r--src/libsystemd-network/arp-util.c154
-rw-r--r--src/libsystemd-network/arp-util.h (renamed from src/libsystemd-network/ipv4ll-internal.h)18
-rw-r--r--src/libsystemd-network/dhcp-identifier.c64
-rw-r--r--src/libsystemd-network/dhcp-identifier.h25
-rw-r--r--src/libsystemd-network/dhcp-internal.h25
-rw-r--r--src/libsystemd-network/dhcp-lease-internal.h69
-rw-r--r--src/libsystemd-network/dhcp-network.c16
-rw-r--r--src/libsystemd-network/dhcp-option.c138
-rw-r--r--src/libsystemd-network/dhcp-packet.c9
-rw-r--r--src/libsystemd-network/dhcp-protocol.h45
-rw-r--r--src/libsystemd-network/dhcp-server-internal.h41
-rw-r--r--src/libsystemd-network/dhcp6-internal.h18
-rw-r--r--src/libsystemd-network/dhcp6-lease-internal.h30
-rw-r--r--src/libsystemd-network/dhcp6-network.c137
-rw-r--r--src/libsystemd-network/dhcp6-option.c126
-rw-r--r--src/libsystemd-network/dhcp6-protocol.h44
-rw-r--r--src/libsystemd-network/icmp6-util.c141
-rw-r--r--src/libsystemd-network/icmp6-util.h (renamed from src/libsystemd-network/lldp-util.h)11
-rw-r--r--src/libsystemd-network/ipv4ll-network.c91
-rw-r--r--src/libsystemd-network/ipv4ll-packet.c71
-rw-r--r--src/libsystemd-network/lldp-internal.c533
-rw-r--r--src/libsystemd-network/lldp-internal.h86
-rw-r--r--src/libsystemd-network/lldp-neighbor.c813
-rw-r--r--src/libsystemd-network/lldp-neighbor.h108
-rw-r--r--src/libsystemd-network/lldp-network.c68
-rw-r--r--src/libsystemd-network/lldp-network.h5
-rw-r--r--src/libsystemd-network/lldp-port.c116
-rw-r--r--src/libsystemd-network/lldp-port.h63
-rw-r--r--src/libsystemd-network/lldp-tlv.c321
-rw-r--r--src/libsystemd-network/lldp-tlv.h87
-rw-r--r--src/libsystemd-network/lldp.h115
-rw-r--r--src/libsystemd-network/ndisc-internal.h49
-rw-r--r--src/libsystemd-network/ndisc-router.c779
-rw-r--r--src/libsystemd-network/ndisc-router.h62
-rw-r--r--src/libsystemd-network/network-internal.c289
-rw-r--r--src/libsystemd-network/network-internal.h23
-rw-r--r--src/libsystemd-network/sd-dhcp-client.c601
-rw-r--r--src/libsystemd-network/sd-dhcp-lease.c979
-rw-r--r--src/libsystemd-network/sd-dhcp-server.c443
-rw-r--r--src/libsystemd-network/sd-dhcp6-client.c492
-rw-r--r--src/libsystemd-network/sd-dhcp6-lease.c225
-rw-r--r--src/libsystemd-network/sd-icmp6-nd.c715
-rw-r--r--src/libsystemd-network/sd-ipv4acd.c526
-rw-r--r--src/libsystemd-network/sd-ipv4ll.c697
-rw-r--r--src/libsystemd-network/sd-lldp.c861
-rw-r--r--src/libsystemd-network/sd-ndisc.c422
-rw-r--r--src/libsystemd-network/sd-pppoe.c802
-rw-r--r--src/libsystemd-network/test-acd.c114
-rw-r--r--src/libsystemd-network/test-dhcp-client.c115
-rw-r--r--src/libsystemd-network/test-dhcp-option.c99
-rw-r--r--src/libsystemd-network/test-dhcp-server.c67
-rw-r--r--src/libsystemd-network/test-dhcp6-client.c182
-rw-r--r--src/libsystemd-network/test-icmp6-rs.c357
-rw-r--r--src/libsystemd-network/test-ipv4ll-manual.c128
-rw-r--r--src/libsystemd-network/test-ipv4ll.c137
-rw-r--r--src/libsystemd-network/test-lldp.c338
-rw-r--r--src/libsystemd-network/test-ndisc-rs.c317
-rw-r--r--src/libsystemd-network/test-pppoe.c176
58 files changed, 7112 insertions, 6471 deletions
diff --git a/src/libsystemd-network/arp-util.c b/src/libsystemd-network/arp-util.c
new file mode 100644
index 0000000000..02028bf28a
--- /dev/null
+++ b/src/libsystemd-network/arp-util.c
@@ -0,0 +1,154 @@
+/***
+ 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 "arp-util.h"
+#include "fd-util.h"
+#include "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 = htobe16(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 = htobe16(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 = htobe16(ARPHRD_ETHER), /* HTYPE */
+ .ea_hdr.ar_pro = htobe16(ETHERTYPE_IP), /* PTYPE */
+ .ea_hdr.ar_hln = ETH_ALEN, /* HLEN */
+ .ea_hdr.ar_pln = sizeof(be32_t), /* PLEN */
+ .ea_hdr.ar_op = htobe16(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..3ef56b002a 100644
--- a/src/libsystemd-network/ipv4ll-internal.h
+++ b/src/libsystemd-network/arp-util.h
@@ -1,5 +1,3 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
#pragma once
/***
@@ -23,16 +21,12 @@
#include <netinet/if_ether.h>
-#include "sparse-endian.h"
#include "socket-util.h"
+#include "sparse-endian.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);
+int arp_network_bind_raw_socket(int index, be32_t address, const struct ether_addr *eth_mac);
-#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/dhcp-identifier.c b/src/libsystemd-network/dhcp-identifier.c
index 70c68ad131..a21efc4d06 100644
--- a/src/libsystemd-network/dhcp-identifier.c
+++ b/src/libsystemd-network/dhcp-identifier.c
@@ -1,5 +1,3 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
/***
This file is part of systemd.
@@ -19,24 +17,54 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-
-#include "sd-id128.h"
#include "libudev.h"
-#include "udev-util.h"
-
-#include "virt.h"
-#include "sparse-endian.h"
-#include "siphash24.h"
+#include "sd-id128.h"
-#include "dhcp6-protocol.h"
#include "dhcp-identifier.h"
+#include "dhcp6-protocol.h"
#include "network-internal.h"
+#include "siphash24.h"
+#include "sparse-endian.h"
+#include "udev-util.h"
+#include "virt.h"
#define SYSTEMD_PEN 43793
#define HASH_KEY SD_ID128_MAKE(80,11,8c,c2,fe,4a,03,ee,3e,d6,0c,6f,36,39,14,09)
+int dhcp_validate_duid_len(uint16_t duid_type, size_t duid_len) {
+ struct duid d;
+
+ assert_cc(sizeof(d.raw) >= MAX_DUID_LEN);
+ if (duid_len > MAX_DUID_LEN)
+ return -EINVAL;
+
+ switch (duid_type) {
+ case DUID_TYPE_LLT:
+ if (duid_len <= sizeof(d.llt))
+ return -EINVAL;
+ break;
+ case DUID_TYPE_EN:
+ if (duid_len != sizeof(d.en))
+ return -EINVAL;
+ break;
+ case DUID_TYPE_LL:
+ if (duid_len <= sizeof(d.ll))
+ return -EINVAL;
+ break;
+ case DUID_TYPE_UUID:
+ if (duid_len != sizeof(d.uuid))
+ return -EINVAL;
+ break;
+ default:
+ /* accept unknown type in order to be forward compatible */
+ break;
+ }
+ return 0;
+}
+
int dhcp_identifier_set_duid_en(struct duid *duid, size_t *len) {
sd_id128_t machine_id;
+ uint64_t hash;
int r;
assert(duid);
@@ -46,19 +74,19 @@ int dhcp_identifier_set_duid_en(struct duid *duid, size_t *len) {
if (r < 0)
return r;
- unaligned_write_be16(&duid->type, DHCP6_DUID_EN);
+ unaligned_write_be16(&duid->type, DUID_TYPE_EN);
unaligned_write_be32(&duid->en.pen, SYSTEMD_PEN);
*len = sizeof(duid->type) + sizeof(duid->en);
/* a bit of snake-oil perhaps, but no need to expose the machine-id
- directly */
- siphash24(duid->en.id, &machine_id, sizeof(machine_id), HASH_KEY.bytes);
+ directly; duid->en.id might not be aligned, so we need to copy */
+ hash = htole64(siphash24(&machine_id, sizeof(machine_id), HASH_KEY.bytes));
+ memcpy(duid->en.id, &hash, sizeof(duid->en.id));
return 0;
}
-
int dhcp_identifier_set_iaid(int ifindex, uint8_t *mac, size_t mac_len, void *_id) {
/* name is a pointer to memory in the udev_device struct, so must
have the same scope */
@@ -66,7 +94,7 @@ int dhcp_identifier_set_iaid(int ifindex, uint8_t *mac, size_t mac_len, void *_i
const char *name = NULL;
uint64_t id;
- if (detect_container(NULL) <= 0) {
+ if (detect_container() <= 0) {
/* not in a container, udev will be around */
_cleanup_udev_unref_ struct udev *udev;
char ifindex_str[2 + DECIMAL_STR_MAX(int)];
@@ -87,10 +115,12 @@ int dhcp_identifier_set_iaid(int ifindex, uint8_t *mac, size_t mac_len, void *_i
}
if (name)
- siphash24((uint8_t*)&id, name, strlen(name), HASH_KEY.bytes);
+ id = siphash24(name, strlen(name), HASH_KEY.bytes);
else
/* fall back to MAC address if no predictable name available */
- siphash24((uint8_t*)&id, mac, mac_len, HASH_KEY.bytes);
+ id = siphash24(mac, mac_len, HASH_KEY.bytes);
+
+ id = htole64(id);
/* fold into 32 bits */
unaligned_write_be32(_id, (id & 0xffffffff) ^ (id >> 32));
diff --git a/src/libsystemd-network/dhcp-identifier.h b/src/libsystemd-network/dhcp-identifier.h
index 95117915f4..1cc0f9fb71 100644
--- a/src/libsystemd-network/dhcp-identifier.h
+++ b/src/libsystemd-network/dhcp-identifier.h
@@ -1,5 +1,3 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
#pragma once
/***
@@ -21,38 +19,48 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
+#include "sd-id128.h"
#include "macro.h"
#include "sparse-endian.h"
#include "unaligned.h"
-#include "sd-id128.h"
+
+typedef enum DUIDType {
+ DUID_TYPE_LLT = 1,
+ DUID_TYPE_EN = 2,
+ DUID_TYPE_LL = 3,
+ DUID_TYPE_UUID = 4,
+ _DUID_TYPE_MAX,
+ _DUID_TYPE_INVALID = -1,
+} DUIDType;
/* RFC 3315 section 9.1:
* A DUID can be no more than 128 octets long (not including the type code).
*/
#define MAX_DUID_LEN 128
+/* https://tools.ietf.org/html/rfc3315#section-9.1 */
struct duid {
- uint16_t type;
+ be16_t type;
union {
struct {
- /* DHCP6_DUID_LLT */
+ /* DUID_TYPE_LLT */
uint16_t htype;
uint32_t time;
uint8_t haddr[0];
} _packed_ llt;
struct {
- /* DHCP6_DUID_EN */
+ /* DUID_TYPE_EN */
uint32_t pen;
uint8_t id[8];
} _packed_ en;
struct {
- /* DHCP6_DUID_LL */
+ /* DUID_TYPE_LL */
int16_t htype;
uint8_t haddr[0];
} _packed_ ll;
struct {
- /* DHCP6_DUID_UUID */
+ /* DUID_TYPE_UUID */
sd_id128_t uuid;
} _packed_ uuid;
struct {
@@ -61,5 +69,6 @@ struct duid {
};
} _packed_;
+int dhcp_validate_duid_len(uint16_t duid_type, size_t duid_len);
int dhcp_identifier_set_duid_en(struct duid *duid, size_t *len);
int dhcp_identifier_set_iaid(int ifindex, uint8_t *mac, size_t mac_len, void *_id);
diff --git a/src/libsystemd-network/dhcp-internal.h b/src/libsystemd-network/dhcp-internal.h
index 7c60ef123c..99f690897d 100644
--- a/src/libsystemd-network/dhcp-internal.h
+++ b/src/libsystemd-network/dhcp-internal.h
@@ -1,5 +1,3 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
#pragma once
/***
@@ -22,15 +20,15 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <stdint.h>
#include <linux/if_packet.h>
-#include <net/if_arp.h>
#include <net/ethernet.h>
-
-#include "socket-util.h"
+#include <net/if_arp.h>
+#include <stdint.h>
#include "sd-dhcp-client.h"
+
#include "dhcp-protocol.h"
+#include "socket-util.h"
int dhcp_network_bind_raw_socket(int index, union sockaddr_union *link,
uint32_t xid, const uint8_t *mac_addr,
@@ -44,11 +42,10 @@ int dhcp_network_send_udp_socket(int s, be32_t address, uint16_t port,
int dhcp_option_append(DHCPMessage *message, size_t size, size_t *offset, uint8_t overload,
uint8_t code, size_t optlen, const void *optval);
-typedef int (*dhcp_option_cb_t)(uint8_t code, uint8_t len,
- const uint8_t *option, void *user_data);
+typedef int (*dhcp_option_callback_t)(uint8_t code, uint8_t len,
+ const void *option, void *userdata);
-int dhcp_option_parse(DHCPMessage *message, size_t len,
- dhcp_option_cb_t cb, void *user_data);
+int dhcp_option_parse(DHCPMessage *message, size_t len, dhcp_option_callback_t cb, void *userdata, char **error_message);
int dhcp_message_init(DHCPMessage *message, uint8_t op, uint32_t xid,
uint8_t type, uint16_t arp_type, size_t optlen,
@@ -62,13 +59,11 @@ void dhcp_packet_append_ip_headers(DHCPPacket *packet, be32_t source_addr,
int dhcp_packet_verify_headers(DHCPPacket *packet, size_t len, bool checksum);
-DEFINE_TRIVIAL_CLEANUP_FUNC(sd_dhcp_client*, sd_dhcp_client_unref);
-#define _cleanup_dhcp_client_unref_ _cleanup_(sd_dhcp_client_unrefp)
-
/* If we are invoking callbacks of a dhcp-client, ensure unreffing the
* client from the callback doesn't destroy the object we are working
* on */
#define DHCP_CLIENT_DONT_DESTROY(client) \
- _cleanup_dhcp_client_unref_ _unused_ sd_dhcp_client *_dont_destroy_##client = sd_dhcp_client_ref(client)
+ _cleanup_(sd_dhcp_client_unrefp) _unused_ sd_dhcp_client *_dont_destroy_##client = sd_dhcp_client_ref(client)
-#define log_dhcp_client(client, fmt, ...) log_internal(LOG_DEBUG, 0, __FILE__, __LINE__, __func__, "DHCP CLIENT (0x%x): " fmt, client->xid, ##__VA_ARGS__)
+#define log_dhcp_client_errno(client, error, fmt, ...) log_internal(LOG_DEBUG, error, __FILE__, __LINE__, __func__, "DHCP CLIENT (0x%x): " fmt, client->xid, ##__VA_ARGS__)
+#define log_dhcp_client(client, fmt, ...) log_dhcp_client_errno(client, 0, fmt, ##__VA_ARGS__)
diff --git a/src/libsystemd-network/dhcp-lease-internal.h b/src/libsystemd-network/dhcp-lease-internal.h
index 6e00b1ad30..82cae2300a 100644
--- a/src/libsystemd-network/dhcp-lease-internal.h
+++ b/src/libsystemd-network/dhcp-lease-internal.h
@@ -1,5 +1,3 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
#pragma once
/***
@@ -25,12 +23,11 @@
#include <stdint.h>
#include <linux/if_packet.h>
-#include "refcnt.h"
-#include "util.h"
+#include "sd-dhcp-client.h"
#include "dhcp-protocol.h"
-
-#include "sd-dhcp-client.h"
+#include "list.h"
+#include "util.h"
struct sd_dhcp_route {
struct in_addr dst_addr;
@@ -38,52 +35,68 @@ struct sd_dhcp_route {
unsigned char dst_prefixlen;
};
+struct sd_dhcp_raw_option {
+ LIST_FIELDS(struct sd_dhcp_raw_option, options);
+
+ uint8_t tag;
+ uint8_t length;
+ void *data;
+};
+
struct sd_dhcp_lease {
- RefCount n_ref;
+ unsigned n_ref;
- int32_t time_offset;
+ /* each 0 if unset */
uint32_t t1;
uint32_t t2;
uint32_t lifetime;
- uint32_t mtu_aging_timeout;
+
+ /* each 0 if unset */
be32_t address;
be32_t server_address;
- be32_t subnet_mask;
be32_t router;
be32_t next_server;
+
+ bool have_subnet_mask;
+ be32_t subnet_mask;
+
+ bool have_broadcast;
be32_t broadcast;
+
struct in_addr *dns;
size_t dns_size;
+
struct in_addr *ntp;
size_t ntp_size;
- struct in_addr *policy_filter;
- size_t policy_filter_size;
+
struct sd_dhcp_route *static_route;
- size_t static_route_size;
- size_t static_route_allocated;
- uint16_t boot_file_size;
- uint16_t mdr;
- uint16_t mtu;
- uint8_t ttl;
- bool ip_forward;
- bool ip_forward_non_local;
+ size_t static_route_size, static_route_allocated;
+
+ uint16_t mtu; /* 0 if unset */
+
char *domainname;
char *hostname;
char *root_path;
- uint8_t *client_id;
+
+ void *client_id;
size_t client_id_len;
- uint8_t *vendor_specific;
+
+ void *vendor_specific;
size_t vendor_specific_len;
+
+ char *timezone;
+
+ LIST_HEAD(struct sd_dhcp_raw_option, private_options);
};
int dhcp_lease_new(sd_dhcp_lease **ret);
-int dhcp_lease_parse_options(uint8_t code, uint8_t len, const uint8_t *option,
- void *user_data);
+
+int dhcp_lease_parse_options(uint8_t code, uint8_t len, const void *option, void *userdata);
+int dhcp_lease_insert_private_option(sd_dhcp_lease *lease, uint8_t tag, const void *data, uint8_t len);
int dhcp_lease_set_default_subnet_mask(sd_dhcp_lease *lease);
-int dhcp_lease_set_client_id(sd_dhcp_lease *lease, const uint8_t *client_id,
- size_t client_id_len);
+int dhcp_lease_set_client_id(sd_dhcp_lease *lease, const void *client_id, size_t client_id_len);
-DEFINE_TRIVIAL_CLEANUP_FUNC(sd_dhcp_lease*, sd_dhcp_lease_unref);
-#define _cleanup_dhcp_lease_unref_ _cleanup_(sd_dhcp_lease_unrefp)
+int dhcp_lease_save(sd_dhcp_lease *lease, const char *lease_file);
+int dhcp_lease_load(sd_dhcp_lease **ret, const char *lease_file);
diff --git a/src/libsystemd-network/dhcp-network.c b/src/libsystemd-network/dhcp-network.c
index 7f10838de1..a9f5a0a5de 100644
--- a/src/libsystemd-network/dhcp-network.c
+++ b/src/libsystemd-network/dhcp-network.c
@@ -18,18 +18,18 @@
***/
#include <errno.h>
-#include <sys/socket.h>
-#include <string.h>
-#include <linux/if_packet.h>
-#include <linux/if_infiniband.h>
#include <net/ethernet.h>
#include <net/if_arp.h>
#include <stdio.h>
+#include <string.h>
+#include <sys/socket.h>
#include <linux/filter.h>
-
-#include "socket-util.h"
+#include <linux/if_infiniband.h>
+#include <linux/if_packet.h>
#include "dhcp-internal.h"
+#include "fd-util.h"
+#include "socket-util.h"
static int _bind_raw_socket(int ifindex, union sockaddr_union *link,
uint32_t xid, const uint8_t *mac_addr,
@@ -107,9 +107,9 @@ static int _bind_raw_socket(int ifindex, union sockaddr_union *link,
return -errno;
link->ll.sll_family = AF_PACKET;
- link->ll.sll_protocol = htons(ETH_P_IP);
+ link->ll.sll_protocol = htobe16(ETH_P_IP);
link->ll.sll_ifindex = ifindex;
- link->ll.sll_hatype = htons(arp_type);
+ link->ll.sll_hatype = htobe16(arp_type);
link->ll.sll_halen = mac_addr_len;
memcpy(link->ll.sll_addr, bcast_addr, mac_addr_len);
diff --git a/src/libsystemd-network/dhcp-option.c b/src/libsystemd-network/dhcp-option.c
index b6110c5f16..c105196334 100644
--- a/src/libsystemd-network/dhcp-option.c
+++ b/src/libsystemd-network/dhcp-option.c
@@ -1,5 +1,3 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
/***
This file is part of systemd.
@@ -19,10 +17,13 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <stdint.h>
-#include <string.h>
#include <errno.h>
+#include <stdint.h>
#include <stdio.h>
+#include <string.h>
+
+#include "alloc-util.h"
+#include "utf8.h"
#include "dhcp-internal.h"
@@ -31,14 +32,14 @@ static int option_append(uint8_t options[], size_t size, size_t *offset,
assert(options);
assert(offset);
- if (code != DHCP_OPTION_END)
+ if (code != SD_DHCP_OPTION_END)
/* always make sure there is space for an END option */
- size --;
+ size--;
switch (code) {
- case DHCP_OPTION_PAD:
- case DHCP_OPTION_END:
+ case SD_DHCP_OPTION_PAD:
+ case SD_DHCP_OPTION_END:
if (size < *offset + 1)
return -ENOBUFS;
@@ -53,12 +54,7 @@ static int option_append(uint8_t options[], size_t size, size_t *offset,
options[*offset] = code;
options[*offset + 1] = optlen;
- if (optlen) {
- assert(optval);
-
- memcpy(&options[*offset + 2], optval, optlen);
- }
-
+ memcpy_safe(&options[*offset + 2], optval, optlen);
*offset += optlen + 2;
break;
@@ -88,7 +84,7 @@ int dhcp_option_append(DHCPMessage *message, size_t size, size_t *offset,
else if (r == -ENOBUFS && (file || sname)) {
/* did not fit, but we have more buffers to try
close the options array and move the offset to its end */
- r = option_append(message->options, size, offset, DHCP_OPTION_END, 0, NULL);
+ r = option_append(message->options, size, offset, SD_DHCP_OPTION_END, 0, NULL);
if (r < 0)
return r;
@@ -109,7 +105,7 @@ int dhcp_option_append(DHCPMessage *message, size_t size, size_t *offset,
} else if (r == -ENOBUFS && sname) {
/* did not fit, but we have more buffers to try
close the file array and move the offset to its end */
- r = option_append(message->options, size, offset, DHCP_OPTION_END, 0, NULL);
+ r = option_append(message->options, size, offset, SD_DHCP_OPTION_END, 0, NULL);
if (r < 0)
return r;
@@ -139,72 +135,84 @@ int dhcp_option_append(DHCPMessage *message, size_t size, size_t *offset,
}
static int parse_options(const uint8_t options[], size_t buflen, uint8_t *overload,
- uint8_t *message_type, dhcp_option_cb_t cb,
- void *user_data) {
+ uint8_t *message_type, char **error_message, dhcp_option_callback_t cb,
+ void *userdata) {
uint8_t code, len;
+ const uint8_t *option;
size_t offset = 0;
while (offset < buflen) {
- switch (options[offset]) {
- case DHCP_OPTION_PAD:
- offset++;
+ code = options[offset ++];
- break;
+ switch (code) {
+ case SD_DHCP_OPTION_PAD:
+ continue;
- case DHCP_OPTION_END:
+ case SD_DHCP_OPTION_END:
return 0;
+ }
+
+ if (buflen < offset + 1)
+ return -ENOBUFS;
- case DHCP_OPTION_MESSAGE_TYPE:
- if (buflen < offset + 3)
- return -ENOBUFS;
+ len = options[offset ++];
- len = options[++offset];
+ if (buflen < offset + len)
+ return -EINVAL;
+
+ option = &options[offset];
+
+ switch (code) {
+ case SD_DHCP_OPTION_MESSAGE_TYPE:
if (len != 1)
return -EINVAL;
if (message_type)
- *message_type = options[++offset];
- else
- offset++;
-
- offset++;
+ *message_type = *option;
break;
- case DHCP_OPTION_OVERLOAD:
- if (buflen < offset + 3)
- return -ENOBUFS;
-
- len = options[++offset];
- if (len != 1)
+ case SD_DHCP_OPTION_ERROR_MESSAGE:
+ if (len == 0)
return -EINVAL;
- if (overload)
- *overload = options[++offset];
- else
- offset++;
+ if (error_message) {
+ _cleanup_free_ char *string = NULL;
- offset++;
+ /* Accept a trailing NUL byte */
+ if (memchr(option, 0, len - 1))
+ return -EINVAL;
- break;
+ string = strndup((const char *) option, len);
+ if (!string)
+ return -ENOMEM;
- default:
- if (buflen < offset + 3)
- return -ENOBUFS;
+ if (!ascii_is_valid(string))
+ return -EINVAL;
- code = options[offset];
- len = options[++offset];
+ free(*error_message);
+ *error_message = string;
+ string = NULL;
+ }
- if (buflen < ++offset + len)
+ break;
+ case SD_DHCP_OPTION_OVERLOAD:
+ if (len != 1)
return -EINVAL;
- if (cb)
- cb(code, len, &options[offset], user_data);
+ if (overload)
+ *overload = *option;
+
+ break;
- offset += len;
+ default:
+ if (cb)
+ cb(code, len, option, userdata);
break;
}
+
+ offset += len;
}
if (offset < buflen)
@@ -213,8 +221,8 @@ static int parse_options(const uint8_t options[], size_t buflen, uint8_t *overlo
return 0;
}
-int dhcp_option_parse(DHCPMessage *message, size_t len,
- dhcp_option_cb_t cb, void *user_data) {
+int dhcp_option_parse(DHCPMessage *message, size_t len, dhcp_option_callback_t cb, void *userdata, char **_error_message) {
+ _cleanup_free_ char *error_message = NULL;
uint8_t overload = 0;
uint8_t message_type = 0;
int r;
@@ -227,27 +235,29 @@ int dhcp_option_parse(DHCPMessage *message, size_t len,
len -= sizeof(DHCPMessage);
- r = parse_options(message->options, len, &overload, &message_type,
- cb, user_data);
+ r = parse_options(message->options, len, &overload, &message_type, &error_message, cb, userdata);
if (r < 0)
return r;
if (overload & DHCP_OVERLOAD_FILE) {
- r = parse_options(message->file, sizeof(message->file),
- NULL, &message_type, cb, user_data);
+ r = parse_options(message->file, sizeof(message->file), NULL, &message_type, &error_message, cb, userdata);
if (r < 0)
return r;
}
if (overload & DHCP_OVERLOAD_SNAME) {
- r = parse_options(message->sname, sizeof(message->sname),
- NULL, &message_type, cb, user_data);
+ r = parse_options(message->sname, sizeof(message->sname), NULL, &message_type, &error_message, cb, userdata);
if (r < 0)
return r;
}
- if (message_type)
- return message_type;
+ if (message_type == 0)
+ return -ENOMSG;
+
+ if (_error_message && IN_SET(message_type, DHCP_NAK, DHCP_DECLINE)) {
+ *_error_message = error_message;
+ error_message = NULL;
+ }
- return -ENOMSG;
+ return message_type;
}
diff --git a/src/libsystemd-network/dhcp-packet.c b/src/libsystemd-network/dhcp-packet.c
index cd7f5095ca..8be774061d 100644
--- a/src/libsystemd-network/dhcp-packet.c
+++ b/src/libsystemd-network/dhcp-packet.c
@@ -19,13 +19,12 @@
***/
#include <errno.h>
-#include <string.h>
#include <net/ethernet.h>
#include <net/if_arp.h>
+#include <string.h>
-
-#include "dhcp-protocol.h"
#include "dhcp-internal.h"
+#include "dhcp-protocol.h"
#define DHCP_CLIENT_MIN_OPTIONS_SIZE 312
@@ -45,7 +44,7 @@ int dhcp_message_init(DHCPMessage *message, uint8_t op, uint32_t xid,
message->magic = htobe32(DHCP_MAGIC_COOKIE);
r = dhcp_option_append(message, optlen, &offset, 0,
- DHCP_OPTION_MESSAGE_TYPE, 1, &type);
+ SD_DHCP_OPTION_MESSAGE_TYPE, 1, &type);
if (r < 0)
return r;
@@ -67,7 +66,7 @@ uint16_t dhcp_packet_checksum(uint8_t *buf, size_t len) {
/* wrap around in one's complement */
sum++;
- buf_64 ++;
+ buf_64++;
}
if (len % sizeof(uint64_t)) {
diff --git a/src/libsystemd-network/dhcp-protocol.h b/src/libsystemd-network/dhcp-protocol.h
index aa37e9b0b5..5cf7abbff9 100644
--- a/src/libsystemd-network/dhcp-protocol.h
+++ b/src/libsystemd-network/dhcp-protocol.h
@@ -1,5 +1,3 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
#pragma once
/***
@@ -21,8 +19,8 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <netinet/udp.h>
#include <netinet/ip.h>
+#include <netinet/udp.h>
#include <stdint.h>
#include "macro.h"
@@ -61,7 +59,7 @@ typedef struct DHCPPacket DHCPPacket;
#define DHCP_IP_UDP_SIZE (int32_t)(sizeof(struct udphdr) + DHCP_IP_SIZE)
#define DHCP_MESSAGE_SIZE (int32_t)(sizeof(DHCPMessage))
#define DHCP_DEFAULT_MIN_SIZE 576 /* the minimum internet hosts must be able to receive */
-#define DHCP_MIN_OPTIONS_SIZE DHCP_DEFAULT_MIN_SIZE - DHCP_IP_UDP_SIZE - DHCP_MESSAGE_SIZE
+#define DHCP_MIN_OPTIONS_SIZE (DHCP_DEFAULT_MIN_SIZE - DHCP_IP_UDP_SIZE - DHCP_MESSAGE_SIZE)
#define DHCP_MAGIC_COOKIE (uint32_t)(0x63825363)
enum {
@@ -105,38 +103,11 @@ enum {
DHCP_OVERLOAD_SNAME = 2,
};
+#define DHCP_MAX_FQDN_LENGTH 255
+
enum {
- DHCP_OPTION_PAD = 0,
- DHCP_OPTION_SUBNET_MASK = 1,
- DHCP_OPTION_TIME_OFFSET = 2,
- DHCP_OPTION_ROUTER = 3,
- DHCP_OPTION_DOMAIN_NAME_SERVER = 6,
- DHCP_OPTION_HOST_NAME = 12,
- DHCP_OPTION_BOOT_FILE_SIZE = 13,
- DHCP_OPTION_DOMAIN_NAME = 15,
- DHCP_OPTION_ROOT_PATH = 17,
- DHCP_OPTION_ENABLE_IP_FORWARDING = 19,
- DHCP_OPTION_ENABLE_IP_FORWARDING_NL = 20,
- DHCP_OPTION_POLICY_FILTER = 21,
- DHCP_OPTION_INTERFACE_MDR = 22,
- DHCP_OPTION_INTERFACE_TTL = 23,
- DHCP_OPTION_INTERFACE_MTU_AGING_TIMEOUT = 24,
- DHCP_OPTION_INTERFACE_MTU = 26,
- DHCP_OPTION_BROADCAST = 28,
- DHCP_OPTION_STATIC_ROUTE = 33,
- DHCP_OPTION_NTP_SERVER = 42,
- DHCP_OPTION_VENDOR_SPECIFIC = 43,
- DHCP_OPTION_REQUESTED_IP_ADDRESS = 50,
- DHCP_OPTION_IP_ADDRESS_LEASE_TIME = 51,
- DHCP_OPTION_OVERLOAD = 52,
- DHCP_OPTION_MESSAGE_TYPE = 53,
- DHCP_OPTION_SERVER_IDENTIFIER = 54,
- DHCP_OPTION_PARAMETER_REQUEST_LIST = 55,
- DHCP_OPTION_MAXIMUM_MESSAGE_SIZE = 57,
- DHCP_OPTION_RENEWAL_T1_TIME = 58,
- DHCP_OPTION_REBINDING_T2_TIME = 59,
- DHCP_OPTION_VENDOR_CLASS_IDENTIFIER = 60,
- DHCP_OPTION_CLIENT_IDENTIFIER = 61,
- DHCP_OPTION_CLASSLESS_STATIC_ROUTE = 121,
- DHCP_OPTION_END = 255,
+ DHCP_FQDN_FLAG_S = (1 << 0),
+ DHCP_FQDN_FLAG_O = (1 << 1),
+ DHCP_FQDN_FLAG_E = (1 << 2),
+ DHCP_FQDN_FLAG_N = (1 << 3),
};
diff --git a/src/libsystemd-network/dhcp-server-internal.h b/src/libsystemd-network/dhcp-server-internal.h
index 58750c4418..0c76956fad 100644
--- a/src/libsystemd-network/dhcp-server-internal.h
+++ b/src/libsystemd-network/dhcp-server-internal.h
@@ -1,4 +1,4 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+#pragma once
/***
This file is part of systemd.
@@ -20,21 +20,17 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#pragma once
-
-#include "sd-event.h"
#include "sd-dhcp-server.h"
+#include "sd-event.h"
+#include "dhcp-internal.h"
#include "hashmap.h"
-#include "refcnt.h"
-#include "util.h"
#include "log.h"
-
-#include "dhcp-internal.h"
+#include "util.h"
typedef struct DHCPClientId {
size_t length;
- uint8_t *data;
+ void *data;
} DHCPClientId;
typedef struct DHCPLease {
@@ -47,7 +43,7 @@ typedef struct DHCPLease {
} DHCPLease;
struct sd_dhcp_server {
- RefCount n_ref;
+ unsigned n_ref;
sd_event *event;
int event_priority;
@@ -55,15 +51,25 @@ struct sd_dhcp_server {
int fd;
int fd_raw;
- int index;
+ int ifindex;
be32_t address;
be32_t netmask;
- be32_t pool_start;
- size_t pool_size;
- size_t next_offer;
+ be32_t subnet;
+ uint32_t pool_offset;
+ uint32_t pool_size;
+
+ char *timezone;
+
+ struct in_addr *ntp, *dns;
+ unsigned n_ntp, n_dns;
+
+ bool emit_router;
Hashmap *leases_by_client_id;
DHCPLease **bound_leases;
+ DHCPLease invalid_lease;
+
+ uint32_t max_lease_time, default_lease_time;
};
typedef struct DHCPRequest {
@@ -75,12 +81,9 @@ typedef struct DHCPRequest {
size_t max_optlen;
be32_t server_id;
be32_t requested_ip;
- int lifetime;
+ uint32_t lifetime;
} DHCPRequest;
-DEFINE_TRIVIAL_CLEANUP_FUNC(sd_dhcp_server*, sd_dhcp_server_unref);
-#define _cleanup_dhcp_server_unref_ _cleanup_(sd_dhcp_server_unrefp)
-
#define log_dhcp_server(client, fmt, ...) log_internal(LOG_DEBUG, 0, __FILE__, __LINE__, __func__, "DHCP SERVER: " fmt, ##__VA_ARGS__)
int dhcp_server_handle_message(sd_dhcp_server *server, DHCPMessage *message,
@@ -89,5 +92,5 @@ int dhcp_server_send_packet(sd_dhcp_server *server,
DHCPRequest *req, DHCPPacket *packet,
int type, size_t optoffset);
-unsigned long client_id_hash_func(const void *p, const uint8_t hash_key[HASH_KEY_SIZE]);
+void client_id_hash_func(const void *p, struct siphash *state);
int client_id_compare_func(const void *_a, const void *_b);
diff --git a/src/libsystemd-network/dhcp6-internal.h b/src/libsystemd-network/dhcp6-internal.h
index 4f54ad89a6..945c3b9721 100644
--- a/src/libsystemd-network/dhcp6-internal.h
+++ b/src/libsystemd-network/dhcp6-internal.h
@@ -1,11 +1,9 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
#pragma once
/***
This file is part of systemd.
- Copyright (C) 2014 Intel Corporation. All rights reserved.
+ Copyright (C) 2014-2015 Intel Corporation. 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
@@ -24,10 +22,11 @@
#include <net/ethernet.h>
#include <netinet/in.h>
-#include "sparse-endian.h"
#include "sd-event.h"
+
#include "list.h"
#include "macro.h"
+#include "sparse-endian.h"
typedef struct DHCP6Address DHCP6Address;
@@ -56,10 +55,8 @@ struct DHCP6IA {
typedef struct DHCP6IA DHCP6IA;
-#define log_dhcp6_client(p, fmt, ...) log_internal(LOG_DEBUG, 0, __FILE__, __LINE__, __func__, "DHCPv6 CLIENT: " fmt, ##__VA_ARGS__)
-
-int dhcp_network_icmp6_bind_router_solicitation(int index);
-int dhcp_network_icmp6_send_router_solicitation(int s, const struct ether_addr *ether_addr);
+#define log_dhcp6_client_errno(p, error, fmt, ...) log_internal(LOG_DEBUG, error, __FILE__, __LINE__, __func__, "DHCPv6 CLIENT: " fmt, ##__VA_ARGS__)
+#define log_dhcp6_client(p, fmt, ...) log_dhcp6_client_errno(p, 0, fmt, ##__VA_ARGS__)
int dhcp6_option_append(uint8_t **buf, size_t *buflen, uint16_t code,
size_t optlen, const void *optval);
@@ -68,6 +65,11 @@ int dhcp6_option_parse(uint8_t **buf, size_t *buflen, uint16_t *optcode,
size_t *optlen, uint8_t **optvalue);
int dhcp6_option_parse_ia(uint8_t **buf, size_t *buflen, uint16_t iatype,
DHCP6IA *ia);
+int dhcp6_option_parse_ip6addrs(uint8_t *optval, uint16_t optlen,
+ struct in6_addr **addrs, size_t count,
+ size_t *allocated);
+int dhcp6_option_parse_domainname(const uint8_t *optval, uint16_t optlen,
+ char ***str_arr);
int dhcp6_network_bind_udp_socket(int index, struct in6_addr *address);
int dhcp6_network_send_udp_socket(int s, struct in6_addr *address,
diff --git a/src/libsystemd-network/dhcp6-lease-internal.h b/src/libsystemd-network/dhcp6-lease-internal.h
index 109e0f4f21..14e708ef63 100644
--- a/src/libsystemd-network/dhcp6-lease-internal.h
+++ b/src/libsystemd-network/dhcp6-lease-internal.h
@@ -1,12 +1,10 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
#pragma once
/***
This file is part of systemd.
Copyright (C) 2014 Tom Gundersen
- Copyright (C) 2014 Intel Corporation. All rights reserved.
+ Copyright (C) 2014-2015 Intel Corporation. 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
@@ -24,13 +22,12 @@
#include <stdint.h>
-#include "refcnt.h"
-
#include "sd-dhcp6-lease.h"
+
#include "dhcp6-internal.h"
struct sd_dhcp6_lease {
- RefCount n_ref;
+ unsigned n_ref;
uint8_t *serverid;
size_t serverid_len;
@@ -40,6 +37,17 @@ struct sd_dhcp6_lease {
DHCP6IA ia;
DHCP6Address *addr_iter;
+
+ struct in6_addr *dns;
+ size_t dns_count;
+ size_t dns_allocated;
+ char **domains;
+ size_t domains_count;
+ struct in6_addr *ntp;
+ size_t ntp_count;
+ size_t ntp_allocated;
+ char **ntp_fqdn;
+ size_t ntp_fqdn_count;
};
int dhcp6_lease_clear_timers(DHCP6IA *ia);
@@ -56,7 +64,11 @@ int dhcp6_lease_get_rapid_commit(sd_dhcp6_lease *lease, bool *rapid_commit);
int dhcp6_lease_get_iaid(sd_dhcp6_lease *lease, be32_t *iaid);
-int dhcp6_lease_new(sd_dhcp6_lease **ret);
+int dhcp6_lease_set_dns(sd_dhcp6_lease *lease, uint8_t *optval, size_t optlen);
+int dhcp6_lease_set_domains(sd_dhcp6_lease *lease, uint8_t *optval,
+ size_t optlen);
+int dhcp6_lease_set_ntp(sd_dhcp6_lease *lease, uint8_t *optval, size_t optlen);
+int dhcp6_lease_set_sntp(sd_dhcp6_lease *lease, uint8_t *optval,
+ size_t optlen) ;
-DEFINE_TRIVIAL_CLEANUP_FUNC(sd_dhcp6_lease*, sd_dhcp6_lease_unref);
-#define _cleanup_dhcp6_lease_free_ _cleanup_(sd_dhcp6_lease_unrefp)
+int dhcp6_lease_new(sd_dhcp6_lease **ret);
diff --git a/src/libsystemd-network/dhcp6-network.c b/src/libsystemd-network/dhcp6-network.c
index fe56c10273..fd2d60c9d5 100644
--- a/src/libsystemd-network/dhcp6-network.c
+++ b/src/libsystemd-network/dhcp6-network.c
@@ -18,150 +18,47 @@
***/
#include <errno.h>
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <string.h>
-#include <linux/if_packet.h>
+#include <netinet/in.h>
+#include <netinet/ip6.h>
#include <stdio.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <sys/types.h>
#include <unistd.h>
-#include <netinet/ip6.h>
-#include <netinet/icmp6.h>
-#include <netinet/in.h>
-
-#include "socket-util.h"
+#include <linux/if_packet.h>
#include "dhcp6-internal.h"
#include "dhcp6-protocol.h"
-
-#define IN6ADDR_ALL_ROUTERS_MULTICAST_INIT \
- { { { 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02 } } }
-
-#define IN6ADDR_ALL_NODES_MULTICAST_INIT \
- { { { 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 } } }
-
-int dhcp_network_icmp6_bind_router_solicitation(int index)
-{
- struct icmp6_filter filter = { };
- struct ipv6_mreq mreq = {
- .ipv6mr_multiaddr = IN6ADDR_ALL_NODES_MULTICAST_INIT,
- .ipv6mr_interface = index,
- };
- _cleanup_close_ int s = -1;
- int r, zero = 0, hops = 255;
-
- s = socket(AF_INET6, SOCK_RAW | SOCK_CLOEXEC | SOCK_NONBLOCK,
- IPPROTO_ICMPV6);
- if (s < 0)
- return -errno;
-
- ICMP6_FILTER_SETBLOCKALL(&filter);
- ICMP6_FILTER_SETPASS(ND_ROUTER_ADVERT, &filter);
- r = setsockopt(s, IPPROTO_ICMPV6, ICMP6_FILTER, &filter,
- sizeof(filter));
- if (r < 0)
- return -errno;
-
- /* RFC 3315, section 6.7, bullet point 2 may indicate that an
- IPV6_PKTINFO socket option also applies for ICMPv6 multicast.
- Empirical experiments indicates otherwise and therefore an
- IPV6_MULTICAST_IF socket option is used here instead */
- r = setsockopt(s, IPPROTO_IPV6, IPV6_MULTICAST_IF, &index,
- sizeof(index));
- if (r < 0)
- return -errno;
-
- r = setsockopt(s, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &zero,
- sizeof(zero));
- if (r < 0)
- return -errno;
-
- r = setsockopt(s, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &hops,
- sizeof(hops));
- if (r < 0)
- return -errno;
-
- r = setsockopt(s, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, &mreq,
- sizeof(mreq));
- if (r < 0)
- return -errno;
-
- r = s;
- s = -1;
- return r;
-}
-
-int dhcp_network_icmp6_send_router_solicitation(int s, const struct ether_addr *ether_addr)
-{
- struct sockaddr_in6 dst = {
- .sin6_family = AF_INET6,
- .sin6_addr = IN6ADDR_ALL_ROUTERS_MULTICAST_INIT,
- };
- struct {
- struct nd_router_solicit rs;
- struct nd_opt_hdr rs_opt;
- struct ether_addr rs_opt_mac;
- } _packed_ rs = {
- .rs.nd_rs_type = ND_ROUTER_SOLICIT,
- };
- struct iovec iov[1] = {
- { &rs, },
- };
- struct msghdr msg = {
- .msg_name = &dst,
- .msg_namelen = sizeof(dst),
- .msg_iov = iov,
- .msg_iovlen = 1,
- };
- int r;
-
- if (ether_addr) {
- memcpy(&rs.rs_opt_mac, ether_addr, ETH_ALEN);
- rs.rs_opt.nd_opt_type = ND_OPT_SOURCE_LINKADDR;
- rs.rs_opt.nd_opt_len = 1;
- iov[0].iov_len = sizeof(rs);
- } else
- iov[0].iov_len = sizeof(rs.rs);
-
- r = sendmsg(s, &msg, 0);
- if (r < 0)
- return -errno;
-
- return 0;
-}
+#include "fd-util.h"
+#include "socket-util.h"
int dhcp6_network_bind_udp_socket(int index, struct in6_addr *local_address) {
- struct in6_pktinfo pktinfo = {
- .ipi6_ifindex = index,
- };
union sockaddr_union src = {
.in6.sin6_family = AF_INET6,
.in6.sin6_port = htobe16(DHCP6_PORT_CLIENT),
- .in6.sin6_addr = IN6ADDR_ANY_INIT,
+ .in6.sin6_scope_id = index,
};
_cleanup_close_ int s = -1;
int r, off = 0, on = 1;
- if (local_address)
- memcpy(&src.in6.sin6_addr, local_address,
- sizeof(src.in6.sin6_addr));
+ assert(index > 0);
+ assert(local_address);
- s = socket(AF_INET6, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK,
- IPPROTO_UDP);
+ src.in6.sin6_addr = *local_address;
+
+ s = socket(AF_INET6, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK, IPPROTO_UDP);
if (s < 0)
return -errno;
- r = setsockopt(s, IPPROTO_IPV6, IPV6_PKTINFO, &pktinfo,
- sizeof(pktinfo));
+ r = setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on));
if (r < 0)
return -errno;
- r = setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on));
+ r = setsockopt(s, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &off, sizeof(off));
if (r < 0)
return -errno;
- r = setsockopt(s, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &off, sizeof(off));
+ r = setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
if (r < 0)
return -errno;
diff --git a/src/libsystemd-network/dhcp6-option.c b/src/libsystemd-network/dhcp6-option.c
index ea863f45e4..5462e03476 100644
--- a/src/libsystemd-network/dhcp6-option.c
+++ b/src/libsystemd-network/dhcp6-option.c
@@ -1,9 +1,7 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
/***
This file is part of systemd.
- Copyright (C) 2014 Intel Corporation. All rights reserved.
+ Copyright (C) 2014-2015 Intel Corporation. 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
@@ -19,16 +17,20 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <netinet/in.h>
#include <errno.h>
+#include <netinet/in.h>
#include <string.h>
-#include "sparse-endian.h"
-#include "unaligned.h"
-#include "util.h"
+#include "sd-dhcp6-client.h"
+#include "alloc-util.h"
#include "dhcp6-internal.h"
#include "dhcp6-protocol.h"
+#include "dns-domain.h"
+#include "sparse-endian.h"
+#include "strv.h"
+#include "unaligned.h"
+#include "util.h"
#define DHCP6_OPTION_IA_NA_LEN 12
#define DHCP6_OPTION_IA_TA_LEN 4
@@ -69,8 +71,7 @@ int dhcp6_option_append(uint8_t **buf, size_t *buflen, uint16_t code,
if (r < 0)
return r;
- if (optval)
- memcpy(*buf, optval, optlen);
+ memcpy_safe(*buf, optval, optlen);
*buf += optlen;
*buflen -= optlen;
@@ -88,11 +89,11 @@ int dhcp6_option_append_ia(uint8_t **buf, size_t *buflen, DHCP6IA *ia) {
assert_return(buf && *buf && buflen && ia, -EINVAL);
switch (ia->type) {
- case DHCP6_OPTION_IA_NA:
+ case SD_DHCP6_OPTION_IA_NA:
len = DHCP6_OPTION_IA_NA_LEN;
break;
- case DHCP6_OPTION_IA_TA:
+ case SD_DHCP6_OPTION_IA_TA:
len = DHCP6_OPTION_IA_TA_LEN;
break;
@@ -115,7 +116,7 @@ int dhcp6_option_append_ia(uint8_t **buf, size_t *buflen, DHCP6IA *ia) {
*buflen -= len;
LIST_FOREACH(addresses, addr, ia->addresses) {
- r = option_append_hdr(buf, buflen, DHCP6_OPTION_IAADDR,
+ r = option_append_hdr(buf, buflen, SD_DHCP6_OPTION_IAADDR,
sizeof(addr->iaaddr));
if (r < 0)
return r;
@@ -194,7 +195,7 @@ int dhcp6_option_parse_ia(uint8_t **buf, size_t *buflen, uint16_t iatype,
assert_return(!ia->addresses, -EINVAL);
switch (iatype) {
- case DHCP6_OPTION_IA_NA:
+ case SD_DHCP6_OPTION_IA_NA:
if (*buflen < DHCP6_OPTION_IA_NA_LEN + sizeof(DHCP6Option) +
sizeof(addr->iaaddr)) {
@@ -217,7 +218,7 @@ int dhcp6_option_parse_ia(uint8_t **buf, size_t *buflen, uint16_t iatype,
break;
- case DHCP6_OPTION_IA_TA:
+ case SD_DHCP6_OPTION_IA_TA:
if (*buflen < DHCP6_OPTION_IA_TA_LEN + sizeof(DHCP6Option) +
sizeof(addr->iaaddr)) {
r = -ENOBUFS;
@@ -245,7 +246,7 @@ int dhcp6_option_parse_ia(uint8_t **buf, size_t *buflen, uint16_t iatype,
while ((r = option_parse_hdr(buf, buflen, &opt, &optlen)) >= 0) {
switch (opt) {
- case DHCP6_OPTION_IAADDR:
+ case SD_DHCP6_OPTION_IAADDR:
addr = new0(DHCP6Address, 1);
if (!addr) {
@@ -272,7 +273,7 @@ int dhcp6_option_parse_ia(uint8_t **buf, size_t *buflen, uint16_t iatype,
break;
- case DHCP6_OPTION_STATUS_CODE:
+ case SD_DHCP6_OPTION_STATUS_CODE:
if (optlen < sizeof(status))
break;
@@ -317,3 +318,96 @@ error:
return r;
}
+
+int dhcp6_option_parse_ip6addrs(uint8_t *optval, uint16_t optlen,
+ struct in6_addr **addrs, size_t count,
+ size_t *allocated) {
+
+ if (optlen == 0 || optlen % sizeof(struct in6_addr) != 0)
+ return -EINVAL;
+
+ if (!GREEDY_REALLOC(*addrs, *allocated,
+ count * sizeof(struct in6_addr) + optlen))
+ return -ENOMEM;
+
+ memcpy(*addrs + count, optval, optlen);
+
+ count += optlen / sizeof(struct in6_addr);
+
+ return count;
+}
+
+int dhcp6_option_parse_domainname(const uint8_t *optval, uint16_t optlen, char ***str_arr) {
+ size_t pos = 0, idx = 0;
+ _cleanup_free_ char **names = NULL;
+ int r;
+
+ assert_return(optlen > 1, -ENODATA);
+ assert_return(optval[optlen - 1] == '\0', -EINVAL);
+
+ while (pos < optlen) {
+ _cleanup_free_ char *ret = NULL;
+ size_t n = 0, allocated = 0;
+ bool first = true;
+
+ for (;;) {
+ uint8_t c;
+
+ c = optval[pos++];
+
+ if (c == 0)
+ /* End of name */
+ break;
+ else if (c <= 63) {
+ const char *label;
+
+ /* Literal label */
+ label = (const char *)&optval[pos];
+ pos += c;
+ if (pos > optlen)
+ return -EMSGSIZE;
+
+ if (!GREEDY_REALLOC(ret, allocated, n + !first + DNS_LABEL_ESCAPED_MAX)) {
+ r = -ENOMEM;
+ goto fail;
+ }
+
+ if (first)
+ first = false;
+ else
+ ret[n++] = '.';
+
+ r = dns_label_escape(label, c, ret + n, DNS_LABEL_ESCAPED_MAX);
+ if (r < 0)
+ goto fail;
+
+ n += r;
+ continue;
+ } else {
+ r = -EBADMSG;
+ goto fail;
+ }
+ }
+
+ if (!GREEDY_REALLOC(ret, allocated, n + 1)) {
+ r = -ENOMEM;
+ goto fail;
+ }
+
+ ret[n] = 0;
+
+ r = strv_extend(&names, ret);
+ if (r < 0)
+ goto fail;
+
+ idx++;
+ }
+
+ *str_arr = names;
+ names = NULL;
+
+ return idx;
+
+fail:
+ return r;
+}
diff --git a/src/libsystemd-network/dhcp6-protocol.h b/src/libsystemd-network/dhcp6-protocol.h
index 3e0f339237..2487c470ab 100644
--- a/src/libsystemd-network/dhcp6-protocol.h
+++ b/src/libsystemd-network/dhcp6-protocol.h
@@ -1,5 +1,3 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
#pragma once
/***
@@ -64,13 +62,6 @@ enum {
#define DHCP6_REB_TIMEOUT 10 * USEC_PER_SEC
#define DHCP6_REB_MAX_RT 600 * USEC_PER_SEC
-enum {
- DHCP6_DUID_LLT = 1,
- DHCP6_DUID_EN = 2,
- DHCP6_DUID_LL = 3,
- DHCP6_DUID_UUID = 4,
-};
-
enum DHCP6State {
DHCP6_STATE_STOPPED = 0,
DHCP6_STATE_INFORMATION_REQUEST = 1,
@@ -99,38 +90,9 @@ enum {
};
enum {
- DHCP6_OPTION_CLIENTID = 1,
- DHCP6_OPTION_SERVERID = 2,
- DHCP6_OPTION_IA_NA = 3,
- DHCP6_OPTION_IA_TA = 4,
- DHCP6_OPTION_IAADDR = 5,
- DHCP6_OPTION_ORO = 6,
- DHCP6_OPTION_PREFERENCE = 7,
- DHCP6_OPTION_ELAPSED_TIME = 8,
- DHCP6_OPTION_RELAY_MSG = 9,
- /* option code 10 is unassigned */
- DHCP6_OPTION_AUTH = 11,
- DHCP6_OPTION_UNICAST = 12,
- DHCP6_OPTION_STATUS_CODE = 13,
- DHCP6_OPTION_RAPID_COMMIT = 14,
- DHCP6_OPTION_USER_CLASS = 15,
- DHCP6_OPTION_VENDOR_CLASS = 16,
- DHCP6_OPTION_VENDOR_OPTS = 17,
- DHCP6_OPTION_INTERFACE_ID = 18,
- DHCP6_OPTION_RECONF_MSG = 19,
- DHCP6_OPTION_RECONF_ACCEPT = 20,
-
- DHCP6_OPTION_DNS_SERVERS = 23, /* RFC 3646 */
- DHCP6_OPTION_DOMAIN_LIST = 24, /* RFC 3646 */
-
- DHCP6_OPTION_SNTP_SERVERS = 31, /* RFC 4075 */
-
- /* option code 35 is unassigned */
-
- DHCP6_OPTION_NTP_SERVER = 56, /* RFC 5908 */
-
- /* option codes 89-142 are unassigned */
- /* option codes 144-65535 are unassigned */
+ DHCP6_NTP_SUBOPTION_SRV_ADDR = 1,
+ DHCP6_NTP_SUBOPTION_MC_ADDR = 2,
+ DHCP6_NTP_SUBOPTION_SRV_FQDN = 3,
};
enum {
diff --git a/src/libsystemd-network/icmp6-util.c b/src/libsystemd-network/icmp6-util.c
new file mode 100644
index 0000000000..c2e4b0e9e3
--- /dev/null
+++ b/src/libsystemd-network/icmp6-util.c
@@ -0,0 +1,141 @@
+/***
+ This file is part of systemd.
+
+ Copyright (C) 2014 Intel Corporation. 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 <errno.h>
+#include <netinet/icmp6.h>
+#include <netinet/in.h>
+#include <netinet/ip6.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <net/if.h>
+#include <linux/if_packet.h>
+
+#include "fd-util.h"
+#include "icmp6-util.h"
+#include "socket-util.h"
+
+#define IN6ADDR_ALL_ROUTERS_MULTICAST_INIT \
+ { { { 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02 } } }
+
+#define IN6ADDR_ALL_NODES_MULTICAST_INIT \
+ { { { 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 } } }
+
+int icmp6_bind_router_solicitation(int index) {
+ struct icmp6_filter filter = { };
+ struct ipv6_mreq mreq = {
+ .ipv6mr_multiaddr = IN6ADDR_ALL_NODES_MULTICAST_INIT,
+ .ipv6mr_interface = index,
+ };
+ _cleanup_close_ int s = -1;
+ char ifname[IF_NAMESIZE] = "";
+ static const int zero = 0, one = 1, hops = 255;
+ int r;
+
+ s = socket(AF_INET6, SOCK_RAW | SOCK_CLOEXEC | SOCK_NONBLOCK, IPPROTO_ICMPV6);
+ if (s < 0)
+ return -errno;
+
+ ICMP6_FILTER_SETBLOCKALL(&filter);
+ ICMP6_FILTER_SETPASS(ND_ROUTER_ADVERT, &filter);
+ r = setsockopt(s, IPPROTO_ICMPV6, ICMP6_FILTER, &filter, sizeof(filter));
+ if (r < 0)
+ return -errno;
+
+ /* RFC 3315, section 6.7, bullet point 2 may indicate that an
+ IPV6_PKTINFO socket option also applies for ICMPv6 multicast.
+ Empirical experiments indicates otherwise and therefore an
+ IPV6_MULTICAST_IF socket option is used here instead */
+ r = setsockopt(s, IPPROTO_IPV6, IPV6_MULTICAST_IF, &index, sizeof(index));
+ if (r < 0)
+ return -errno;
+
+ r = setsockopt(s, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &zero, sizeof(zero));
+ if (r < 0)
+ return -errno;
+
+ r = setsockopt(s, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &hops, sizeof(hops));
+ if (r < 0)
+ return -errno;
+
+ r = setsockopt(s, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, &mreq, sizeof(mreq));
+ if (r < 0)
+ return -errno;
+
+ r = setsockopt(s, SOL_IPV6, IPV6_RECVHOPLIMIT, &one, sizeof(one));
+ if (r < 0)
+ return -errno;
+
+ r = setsockopt(s, SOL_SOCKET, SO_TIMESTAMP, &one, sizeof(one));
+ if (r < 0)
+ return -errno;
+
+ if (if_indextoname(index, ifname) == 0)
+ return -errno;
+
+ r = setsockopt(s, SOL_SOCKET, SO_BINDTODEVICE, ifname, strlen(ifname));
+ if (r < 0)
+ return -errno;
+
+ r = s;
+ s = -1;
+ return r;
+}
+
+int icmp6_send_router_solicitation(int s, const struct ether_addr *ether_addr) {
+ struct sockaddr_in6 dst = {
+ .sin6_family = AF_INET6,
+ .sin6_addr = IN6ADDR_ALL_ROUTERS_MULTICAST_INIT,
+ };
+ struct {
+ struct nd_router_solicit rs;
+ struct nd_opt_hdr rs_opt;
+ struct ether_addr rs_opt_mac;
+ } _packed_ rs = {
+ .rs.nd_rs_type = ND_ROUTER_SOLICIT,
+ .rs_opt.nd_opt_type = ND_OPT_SOURCE_LINKADDR,
+ .rs_opt.nd_opt_len = 1,
+ };
+ struct iovec iov = {
+ .iov_base = &rs,
+ .iov_len = sizeof(rs),
+ };
+ struct msghdr msg = {
+ .msg_name = &dst,
+ .msg_namelen = sizeof(dst),
+ .msg_iov = &iov,
+ .msg_iovlen = 1,
+ };
+ int r;
+
+ assert(s >= 0);
+ assert(ether_addr);
+
+ rs.rs_opt_mac = *ether_addr;
+
+ r = sendmsg(s, &msg, 0);
+ if (r < 0)
+ return -errno;
+
+ return 0;
+}
diff --git a/src/libsystemd-network/lldp-util.h b/src/libsystemd-network/icmp6-util.h
index 112001e4b9..2b4dbc76ce 100644
--- a/src/libsystemd-network/lldp-util.h
+++ b/src/libsystemd-network/icmp6-util.h
@@ -1,10 +1,9 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+#pragma once
/***
This file is part of systemd.
- Copyright (C) 2014 Tom Gundersen
- Copyright (C) 2014 Susant Sahani
+ Copyright (C) 2014-2015 Intel Corporation. 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
@@ -20,7 +19,7 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#pragma once
+#include <net/ethernet.h>
-DEFINE_TRIVIAL_CLEANUP_FUNC(sd_lldp *, sd_lldp_free);
-#define _cleanup_lldp_free_ _cleanup_(sd_lldp_freep)
+int icmp6_bind_router_solicitation(int index);
+int icmp6_send_router_solicitation(int s, const struct ether_addr *ether_addr);
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/lldp-internal.c b/src/libsystemd-network/lldp-internal.c
deleted file mode 100644
index 0f354461f7..0000000000
--- a/src/libsystemd-network/lldp-internal.c
+++ /dev/null
@@ -1,533 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- This file is part of systemd.
-
- Copyright (C) 2014 Tom Gundersen
- Copyright (C) 2014 Susant Sahani
-
- 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 "lldp-internal.h"
-
-/* We store maximum 1K chassis entries */
-#define LLDP_MIB_MAX_CHASSIS 1024
-
-/* Maximum Ports can be attached to any chassis */
-#define LLDP_MIB_MAX_PORT_PER_CHASSIS 32
-
-int lldp_read_chassis_id(tlv_packet *tlv,
- uint8_t *type,
- uint16_t *length,
- uint8_t **data) {
- uint8_t subtype;
- int r;
-
- assert_return(tlv, -EINVAL);
-
- r = lldp_tlv_packet_enter_container(tlv, LLDP_TYPE_CHASSIS_ID);
- if (r < 0)
- goto out2;
-
- r = tlv_packet_read_u8(tlv, &subtype);
- if (r < 0)
- goto out1;
-
- switch (subtype) {
- case LLDP_CHASSIS_SUBTYPE_MAC_ADDRESS:
-
- r = tlv_packet_read_bytes(tlv, data, length);
- if (r < 0)
- goto out1;
-
- break;
- default:
- r = -EOPNOTSUPP;
- break;
- }
-
- *type = subtype;
-
- out1:
- (void) lldp_tlv_packet_exit_container(tlv);
-
- out2:
- return r;
-}
-
-int lldp_read_port_id(tlv_packet *tlv,
- uint8_t *type,
- uint16_t *length,
- uint8_t **data) {
- uint8_t subtype;
- char *s;
- int r;
-
- assert_return(tlv, -EINVAL);
-
- r = lldp_tlv_packet_enter_container(tlv, LLDP_TYPE_PORT_ID);
- if (r < 0)
- goto out2;
-
- r = tlv_packet_read_u8(tlv, &subtype);
- if (r < 0)
- goto out1;
-
- switch (subtype) {
- case LLDP_PORT_SUBTYPE_PORT_COMPONENT:
- case LLDP_PORT_SUBTYPE_INTERFACE_ALIAS:
- case LLDP_PORT_SUBTYPE_INTERFACE_NAME:
- case LLDP_PORT_SUBTYPE_LOCALLY_ASSIGNED:
-
- r = tlv_packet_read_string(tlv, &s, length);
- if (r < 0)
- goto out1;
-
- *data = (uint8_t *) s;
-
- break;
- case LLDP_PORT_SUBTYPE_MAC_ADDRESS:
-
- r = tlv_packet_read_bytes(tlv, data, length);
- if (r < 0)
- goto out1;
-
- break;
- default:
- r = -EOPNOTSUPP;
- break;
- }
-
- *type = subtype;
-
- out1:
- (void) lldp_tlv_packet_exit_container(tlv);
-
- out2:
- return r;
-}
-
-int lldp_read_ttl(tlv_packet *tlv, uint16_t *ttl) {
- int r;
-
- assert_return(tlv, -EINVAL);
-
- r = lldp_tlv_packet_enter_container(tlv, LLDP_TYPE_TTL);
- if (r < 0)
- goto out;
-
- r = tlv_packet_read_u16(tlv, ttl);
-
- (void) lldp_tlv_packet_exit_container(tlv);
-
- out:
- return r;
-}
-
-int lldp_read_system_name(tlv_packet *tlv,
- uint16_t *length,
- char **data) {
- char *s;
- int r;
-
- assert_return(tlv, -EINVAL);
-
- r = lldp_tlv_packet_enter_container(tlv, LLDP_TYPE_SYSTEM_NAME);
- if (r < 0)
- return r;
-
- r = tlv_packet_read_string(tlv, &s, length);
- if (r < 0)
- goto out;
-
- *data = (char *) s;
-
- out:
- (void) lldp_tlv_packet_exit_container(tlv);
-
- return r;
-}
-
-int lldp_read_system_description(tlv_packet *tlv,
- uint16_t *length,
- char **data) {
- char *s;
- int r;
-
- assert_return(tlv, -EINVAL);
-
- r = lldp_tlv_packet_enter_container(tlv, LLDP_TYPE_SYSTEM_DESCRIPTION);
- if (r < 0)
- return r;
-
- r = tlv_packet_read_string(tlv, &s, length);
- if (r < 0)
- goto out;
-
- *data = (char *) s;
-
- out:
- (void) lldp_tlv_packet_exit_container(tlv);
-
- return r;
-}
-
-int lldp_read_port_description(tlv_packet *tlv,
- uint16_t *length,
- char **data) {
- char *s;
- int r;
-
- assert_return(tlv, -EINVAL);
-
- r = lldp_tlv_packet_enter_container(tlv, LLDP_TYPE_PORT_DESCRIPTION);
- if (r < 0)
- return r;
-
- r = tlv_packet_read_string(tlv, &s, length);
- if (r < 0)
- goto out;
-
- *data = (char *) s;
-
- out:
- (void) lldp_tlv_packet_exit_container(tlv);
-
- return r;
-}
-
-int lldp_read_system_capability(tlv_packet *tlv, uint16_t *data) {
- int r;
-
- assert_return(tlv, -EINVAL);
-
- r = lldp_tlv_packet_enter_container(tlv, LLDP_TYPE_SYSTEM_CAPABILITIES);
- if (r < 0)
- return r;
-
- r = tlv_packet_read_u16(tlv, data);
- if (r < 0)
- goto out;
-
- return 0;
- out:
-
- (void) lldp_tlv_packet_exit_container(tlv);
-
- return r;
-}
-
-/* 10.5.5.2.2 mibUpdateObjects ()
- * The mibUpdateObjects () procedure updates the MIB objects corresponding to
- * the TLVs contained in the received LLDPDU for the LLDP remote system
- * indicated by the LLDP remote systems update process defined in 10.3.5 */
-
-int lldp_mib_update_objects(lldp_chassis *c, tlv_packet *tlv) {
- lldp_neighbour_port *p;
- uint16_t length, ttl;
- uint8_t *data;
- uint8_t type;
- int r;
-
- assert_return(c, -EINVAL);
- assert_return(tlv, -EINVAL);
-
- r = lldp_read_port_id(tlv, &type, &length, &data);
- if (r < 0)
- return r;
-
- /* Update the packet if we already have */
- LIST_FOREACH(port, p, c->ports) {
-
- if ((p->type == type && p->length == length && !memcmp(p->data, data, p->length))) {
-
- r = lldp_read_ttl(tlv, &ttl);
- if (r < 0)
- return r;
-
- p->until = ttl * USEC_PER_SEC + now(clock_boottime_or_monotonic());
-
- tlv_packet_free(p->packet);
- p->packet = tlv;
-
- prioq_reshuffle(p->c->by_expiry, p, &p->prioq_idx);
-
- return 0;
- }
- }
-
- return -1;
-}
-
-int lldp_mib_remove_objects(lldp_chassis *c, tlv_packet *tlv) {
- lldp_neighbour_port *p, *q;
- uint8_t *data;
- uint16_t length;
- uint8_t type;
- int r;
-
- assert_return(c, -EINVAL);
- assert_return(tlv, -EINVAL);
-
- r = lldp_read_port_id(tlv, &type, &length, &data);
- if (r < 0)
- return r;
-
- LIST_FOREACH_SAFE(port, p, q, c->ports) {
-
- /* Find the port */
- if (p->type == type && p->length == length && !memcmp(p->data, data, p->length)) {
- lldp_neighbour_port_remove_and_free(p);
- break;
- }
- }
-
- return 0;
-}
-
-int lldp_mib_add_objects(Prioq *by_expiry,
- Hashmap *neighbour_mib,
- tlv_packet *tlv) {
- _cleanup_lldp_neighbour_port_free_ lldp_neighbour_port *p = NULL;
- _cleanup_lldp_chassis_free_ lldp_chassis *c = NULL;
- lldp_chassis_id chassis_id;
- bool new_chassis = false;
- uint8_t subtype, *data;
- uint16_t ttl, length;
- int r;
-
- assert_return(by_expiry, -EINVAL);
- assert_return(neighbour_mib, -EINVAL);
- assert_return(tlv, -EINVAL);
-
- r = lldp_read_chassis_id(tlv, &subtype, &length, &data);
- if (r < 0)
- goto drop;
-
- r = lldp_read_ttl(tlv, &ttl);
- if (r < 0)
- goto drop;
-
- /* Make hash key */
- chassis_id.type = subtype;
- chassis_id.length = length;
- chassis_id.data = data;
-
- /* Try to find the Chassis */
- c = hashmap_get(neighbour_mib, &chassis_id);
- if (!c) {
-
- /* Don't create chassis if ttl 0 is received . Silently drop it */
- if (ttl == 0) {
- log_lldp("TTL value 0 received. Skiping Chassis creation.");
- goto drop;
- }
-
- /* Admission Control: Can we store this packet ? */
- if (hashmap_size(neighbour_mib) >= LLDP_MIB_MAX_CHASSIS) {
-
- log_lldp("Exceeding number of chassie: %d. Dropping ...",
- hashmap_size(neighbour_mib));
- goto drop;
- }
-
- r = lldp_chassis_new(tlv, by_expiry, neighbour_mib, &c);
- if (r < 0)
- goto drop;
-
- new_chassis = true;
-
- r = hashmap_put(neighbour_mib, &c->chassis_id, c);
- if (r < 0)
- goto drop;
-
- } else {
-
- /* When the TTL field is set to zero, the receiving LLDP agent is notified all
- * system information associated with the LLDP agent/port is to be deleted */
- if (ttl == 0) {
- log_lldp("TTL value 0 received . Deleting associated Port ...");
-
- lldp_mib_remove_objects(c, tlv);
-
- c = NULL;
- goto drop;
- }
-
- /* if we already have this port just update it */
- r = lldp_mib_update_objects(c, tlv);
- if (r >= 0) {
- c = NULL;
- return r;
- }
-
- /* Admission Control: Can this port attached to the existing chassis ? */
- if (REFCNT_GET(c->n_ref) >= LLDP_MIB_MAX_PORT_PER_CHASSIS) {
- log_lldp("Port limit reached. Chassis has: %d ports. Dropping ...",
- REFCNT_GET(c->n_ref));
-
- c = NULL;
- goto drop;
- }
- }
-
- /* This is a new port */
- r = lldp_neighbour_port_new(c, tlv, &p);
- if (r < 0)
- goto drop;
-
- r = prioq_put(c->by_expiry, p, &p->prioq_idx);
- if (r < 0)
- goto drop;
-
- /* Attach new port to chassis */
- LIST_PREPEND(port, c->ports, p);
- REFCNT_INC(c->n_ref);
-
- p = NULL;
- c = NULL;
-
- return 0;
-
- drop:
- tlv_packet_free(tlv);
-
- if (new_chassis)
- hashmap_remove(neighbour_mib, &c->chassis_id);
-
- return r;
-}
-
-void lldp_neighbour_port_remove_and_free(lldp_neighbour_port *p) {
- lldp_chassis *c;
-
- assert(p);
- assert(p->c);
-
- c = p->c;
-
- prioq_remove(c->by_expiry, p, &p->prioq_idx);
-
- LIST_REMOVE(port, c->ports, p);
- lldp_neighbour_port_free(p);
-
- /* Drop the Chassis if no port is attached */
- if (REFCNT_DEC(c->n_ref) <= 1) {
- hashmap_remove(c->neighbour_mib, &c->chassis_id);
- lldp_chassis_free(c);
- }
-}
-
-void lldp_neighbour_port_free(lldp_neighbour_port *p) {
-
- if(!p)
- return;
-
- tlv_packet_free(p->packet);
-
- free(p->data);
- free(p);
-}
-
-int lldp_neighbour_port_new(lldp_chassis *c,
- tlv_packet *tlv,
- lldp_neighbour_port **ret) {
- _cleanup_lldp_neighbour_port_free_ lldp_neighbour_port *p = NULL;
- uint16_t length, ttl;
- uint8_t *data;
- uint8_t type;
- int r;
-
- assert(tlv);
-
- r = lldp_read_port_id(tlv, &type, &length, &data);
- if (r < 0)
- return r;
-
- r = lldp_read_ttl(tlv, &ttl);
- if (r < 0)
- return r;
-
- p = new0(lldp_neighbour_port, 1);
- if (!p)
- return -ENOMEM;
-
- p->c = c;
- p->type = type;
- p->length = length;
- p->packet = tlv;
- p->prioq_idx = PRIOQ_IDX_NULL;
- p->until = ttl * USEC_PER_SEC + now(clock_boottime_or_monotonic());
-
- p->data = memdup(data, length);
- if (!p->data)
- return -ENOMEM;
-
- *ret = p;
- p = NULL;
-
- return 0;
-}
-
-void lldp_chassis_free(lldp_chassis *c) {
-
- if (!c)
- return;
-
- if (REFCNT_GET(c->n_ref) > 1)
- return;
-
- free(c->chassis_id.data);
- free(c);
-}
-
-int lldp_chassis_new(tlv_packet *tlv,
- Prioq *by_expiry,
- Hashmap *neighbour_mib,
- lldp_chassis **ret) {
- _cleanup_lldp_chassis_free_ lldp_chassis *c = NULL;
- uint16_t length;
- uint8_t *data;
- uint8_t type;
- int r;
-
- assert(tlv);
-
- r = lldp_read_chassis_id(tlv, &type, &length, &data);
- if (r < 0)
- return r;
-
- c = new0(lldp_chassis, 1);
- if (!c)
- return -ENOMEM;
-
- c->n_ref = REFCNT_INIT;
- c->chassis_id.type = type;
- c->chassis_id.length = length;
-
- c->chassis_id.data = memdup(data, length);
- if (!c->chassis_id.data)
- return -ENOMEM;
-
- LIST_HEAD_INIT(c->ports);
-
- c->by_expiry = by_expiry;
- c->neighbour_mib = neighbour_mib;
-
- *ret = c;
- c = NULL;
-
- return 0;
-}
diff --git a/src/libsystemd-network/lldp-internal.h b/src/libsystemd-network/lldp-internal.h
index 8e09ee8f3a..becc162fab 100644
--- a/src/libsystemd-network/lldp-internal.h
+++ b/src/libsystemd-network/lldp-internal.h
@@ -1,4 +1,4 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+#pragma once
/***
This file is part of systemd.
@@ -20,80 +20,36 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#pragma once
+#include "sd-event.h"
+#include "sd-lldp.h"
+#include "hashmap.h"
#include "log.h"
-#include "list.h"
-#include "refcnt.h"
-#include "lldp-tlv.h"
#include "prioq.h"
-typedef struct lldp_neighbour_port lldp_neighbour_port;
-typedef struct lldp_chassis lldp_chassis;
-typedef struct lldp_chassis_id lldp_chassis_id;
-typedef struct lldp_agent_statistics lldp_agent_statistics;
-
-struct lldp_neighbour_port {
- uint8_t type;
- uint8_t *data;
-
- uint16_t length;
- usec_t until;
+struct sd_lldp {
+ unsigned n_ref;
- unsigned prioq_idx;
+ int ifindex;
+ int fd;
- lldp_chassis *c;
- tlv_packet *packet;
+ sd_event *event;
+ int64_t event_priority;
+ sd_event_source *io_event_source;
+ sd_event_source *timer_event_source;
- LIST_FIELDS(lldp_neighbour_port, port);
-};
+ Prioq *neighbor_by_expiry;
+ Hashmap *neighbor_by_id;
-int lldp_neighbour_port_new(lldp_chassis *c, tlv_packet *tlv, lldp_neighbour_port **ret);
-void lldp_neighbour_port_free(lldp_neighbour_port *p);
-void lldp_neighbour_port_remove_and_free(lldp_neighbour_port *p);
+ uint64_t neighbors_max;
-DEFINE_TRIVIAL_CLEANUP_FUNC(lldp_neighbour_port *, lldp_neighbour_port_free);
-#define _cleanup_lldp_neighbour_port_free_ _cleanup_(lldp_neighbour_port_freep)
+ sd_lldp_callback_t callback;
+ void *userdata;
-struct lldp_chassis_id {
- uint8_t type;
- uint16_t length;
+ uint16_t capability_mask;
- uint8_t *data;
+ struct ether_addr filter_address;
};
-struct lldp_chassis {
- RefCount n_ref;
-
- lldp_chassis_id chassis_id;
-
- Prioq *by_expiry;
- Hashmap *neighbour_mib;
-
- LIST_HEAD(lldp_neighbour_port, ports);
-};
-
-int lldp_chassis_new(tlv_packet *tlv,
- Prioq *by_expiry,
- Hashmap *neighbour_mib,
- lldp_chassis **ret);
-
-void lldp_chassis_free(lldp_chassis *c);
-
-DEFINE_TRIVIAL_CLEANUP_FUNC(lldp_chassis *, lldp_chassis_free);
-#define _cleanup_lldp_chassis_free_ _cleanup_(lldp_chassis_freep)
-
-int lldp_mib_update_objects(lldp_chassis *c, tlv_packet *tlv);
-int lldp_mib_add_objects(Prioq *by_expiry, Hashmap *neighbour_mib, tlv_packet *tlv);
-int lldp_mib_remove_objects(lldp_chassis *c, tlv_packet *tlv);
-
-int lldp_read_chassis_id(tlv_packet *tlv, uint8_t *type, uint16_t *length, uint8_t **data);
-int lldp_read_port_id(tlv_packet *tlv, uint8_t *type, uint16_t *length, uint8_t **data);
-int lldp_read_ttl(tlv_packet *tlv, uint16_t *ttl);
-int lldp_read_system_name(tlv_packet *tlv, uint16_t *length, char **data);
-int lldp_read_system_description(tlv_packet *tlv, uint16_t *length, char **data);
-int lldp_read_system_capability(tlv_packet *tlv, uint16_t *data);
-int lldp_read_port_description(tlv_packet *tlv, uint16_t *length, char **data);
-
-int lldp_handle_packet(tlv_packet *m, uint16_t length);
-#define log_lldp(fmt, ...) log_internal(LOG_DEBUG, 0, __FILE__, __LINE__, __func__, "LLDP: " fmt, ##__VA_ARGS__)
+#define log_lldp_errno(error, fmt, ...) log_internal(LOG_DEBUG, error, __FILE__, __LINE__, __func__, "LLDP: " fmt, ##__VA_ARGS__)
+#define log_lldp(fmt, ...) log_lldp_errno(0, fmt, ##__VA_ARGS__)
diff --git a/src/libsystemd-network/lldp-neighbor.c b/src/libsystemd-network/lldp-neighbor.c
new file mode 100644
index 0000000000..53e29377b3
--- /dev/null
+++ b/src/libsystemd-network/lldp-neighbor.c
@@ -0,0 +1,813 @@
+/***
+ This file is part of systemd.
+
+ Copyright 2016 Lennart Poettering
+
+ 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 "alloc-util.h"
+#include "escape.h"
+#include "ether-addr-util.h"
+#include "hexdecoct.h"
+#include "in-addr-util.h"
+#include "lldp-internal.h"
+#include "lldp-neighbor.h"
+#include "unaligned.h"
+
+static void lldp_neighbor_id_hash_func(const void *p, struct siphash *state) {
+ const LLDPNeighborID *id = p;
+
+ siphash24_compress(id->chassis_id, id->chassis_id_size, state);
+ siphash24_compress(&id->chassis_id_size, sizeof(id->chassis_id_size), state);
+ siphash24_compress(id->port_id, id->port_id_size, state);
+ siphash24_compress(&id->port_id_size, sizeof(id->port_id_size), state);
+}
+
+static int lldp_neighbor_id_compare_func(const void *a, const void *b) {
+ const LLDPNeighborID *x = a, *y = b;
+ int r;
+
+ r = memcmp(x->chassis_id, y->chassis_id, MIN(x->chassis_id_size, y->chassis_id_size));
+ if (r != 0)
+ return r;
+
+ if (x->chassis_id_size < y->chassis_id_size)
+ return -1;
+
+ if (x->chassis_id_size > y->chassis_id_size)
+ return 1;
+
+ r = memcmp(x->port_id, y->port_id, MIN(x->port_id_size, y->port_id_size));
+ if (r != 0)
+ return r;
+
+ if (x->port_id_size < y->port_id_size)
+ return -1;
+ if (x->port_id_size > y->port_id_size)
+ return 1;
+
+ return 0;
+}
+
+const struct hash_ops lldp_neighbor_id_hash_ops = {
+ .hash = lldp_neighbor_id_hash_func,
+ .compare = lldp_neighbor_id_compare_func
+};
+
+int lldp_neighbor_prioq_compare_func(const void *a, const void *b) {
+ const sd_lldp_neighbor *x = a, *y = b;
+
+ if (x->until < y->until)
+ return -1;
+
+ if (x->until > y->until)
+ return 1;
+
+ return 0;
+}
+
+_public_ sd_lldp_neighbor *sd_lldp_neighbor_ref(sd_lldp_neighbor *n) {
+ if (!n)
+ return NULL;
+
+ assert(n->n_ref > 0 || n->lldp);
+ n->n_ref++;
+
+ return n;
+}
+
+static void lldp_neighbor_free(sd_lldp_neighbor *n) {
+ assert(n);
+
+ free(n->id.port_id);
+ free(n->id.chassis_id);
+ free(n->port_description);
+ free(n->system_name);
+ free(n->system_description);
+ free(n->chassis_id_as_string);
+ free(n->port_id_as_string);
+ free(n);
+}
+
+_public_ sd_lldp_neighbor *sd_lldp_neighbor_unref(sd_lldp_neighbor *n) {
+
+ /* Drops one reference from the neighbor. Note that the object is not freed unless it is already unlinked from
+ * the sd_lldp object. */
+
+ if (!n)
+ return NULL;
+
+ assert(n->n_ref > 0);
+ n->n_ref--;
+
+ if (n->n_ref <= 0 && !n->lldp)
+ lldp_neighbor_free(n);
+
+ return NULL;
+}
+
+sd_lldp_neighbor *lldp_neighbor_unlink(sd_lldp_neighbor *n) {
+
+ /* Removes the neighbor object from the LLDP object, and frees it if it also has no other reference. */
+
+ if (!n)
+ return NULL;
+
+ if (!n->lldp)
+ return NULL;
+
+ assert_se(hashmap_remove(n->lldp->neighbor_by_id, &n->id) == n);
+ assert_se(prioq_remove(n->lldp->neighbor_by_expiry, n, &n->prioq_idx) >= 0);
+
+ n->lldp = NULL;
+
+ if (n->n_ref <= 0)
+ lldp_neighbor_free(n);
+
+ return NULL;
+}
+
+sd_lldp_neighbor *lldp_neighbor_new(size_t raw_size) {
+ sd_lldp_neighbor *n;
+
+ n = malloc0(ALIGN(sizeof(sd_lldp_neighbor)) + raw_size);
+ if (!n)
+ return NULL;
+
+ n->raw_size = raw_size;
+ n->n_ref = 1;
+
+ return n;
+}
+
+static int parse_string(char **s, const void *q, size_t n) {
+ const char *p = q;
+ char *k;
+
+ assert(s);
+ assert(p || n == 0);
+
+ if (*s) {
+ log_lldp("Found duplicate string, ignoring field.");
+ return 0;
+ }
+
+ /* Strip trailing NULs, just to be nice */
+ while (n > 0 && p[n-1] == 0)
+ n--;
+
+ if (n <= 0) /* Ignore empty strings */
+ return 0;
+
+ /* Look for inner NULs */
+ if (memchr(p, 0, n)) {
+ log_lldp("Found inner NUL in string, ignoring field.");
+ return 0;
+ }
+
+ /* Let's escape weird chars, for security reasons */
+ k = cescape_length(p, n);
+ if (!k)
+ return -ENOMEM;
+
+ free(*s);
+ *s = k;
+
+ return 1;
+}
+
+int lldp_neighbor_parse(sd_lldp_neighbor *n) {
+ struct ether_header h;
+ const uint8_t *p;
+ size_t left;
+ int r;
+
+ assert(n);
+
+ if (n->raw_size < sizeof(struct ether_header)) {
+ log_lldp("Received truncated packet, ignoring.");
+ return -EBADMSG;
+ }
+
+ memcpy(&h, LLDP_NEIGHBOR_RAW(n), sizeof(h));
+
+ if (h.ether_type != htobe16(ETHERTYPE_LLDP)) {
+ log_lldp("Received packet with wrong type, ignoring.");
+ return -EBADMSG;
+ }
+
+ if (h.ether_dhost[0] != 0x01 ||
+ h.ether_dhost[1] != 0x80 ||
+ h.ether_dhost[2] != 0xc2 ||
+ h.ether_dhost[3] != 0x00 ||
+ h.ether_dhost[4] != 0x00 ||
+ !IN_SET(h.ether_dhost[5], 0x00, 0x03, 0x0e)) {
+ log_lldp("Received packet with wrong destination address, ignoring.");
+ return -EBADMSG;
+ }
+
+ memcpy(&n->source_address, h.ether_shost, sizeof(struct ether_addr));
+ memcpy(&n->destination_address, h.ether_dhost, sizeof(struct ether_addr));
+
+ p = (const uint8_t*) LLDP_NEIGHBOR_RAW(n) + sizeof(struct ether_header);
+ left = n->raw_size - sizeof(struct ether_header);
+
+ for (;;) {
+ uint8_t type;
+ uint16_t length;
+
+ if (left < 2) {
+ log_lldp("TLV lacks header, ignoring.");
+ return -EBADMSG;
+ }
+
+ type = p[0] >> 1;
+ length = p[1] + (((uint16_t) (p[0] & 1)) << 8);
+ p += 2, left -= 2;
+
+ if (left < length) {
+ log_lldp("TLV truncated, ignoring datagram.");
+ return -EBADMSG;
+ }
+
+ switch (type) {
+
+ case SD_LLDP_TYPE_END:
+ if (length != 0) {
+ log_lldp("End marker TLV not zero-sized, ignoring datagram.");
+ return -EBADMSG;
+ }
+ if (left != 0) {
+ log_lldp("Trailing garbage in datagram, ignoring datagram.");
+ return -EBADMSG;
+ }
+
+ goto end_marker;
+
+ case SD_LLDP_TYPE_CHASSIS_ID:
+ if (length < 2 || length > 256) { /* includes the chassis subtype, hence one extra byte */
+ log_lldp("Chassis ID field size out of range, ignoring datagram.");
+ return -EBADMSG;
+ }
+ if (n->id.chassis_id) {
+ log_lldp("Duplicate chassis ID field, ignoring datagram.");
+ return -EBADMSG;
+ }
+
+ n->id.chassis_id = memdup(p, length);
+ if (!n->id.chassis_id)
+ return -ENOMEM;
+
+ n->id.chassis_id_size = length;
+ break;
+
+ case SD_LLDP_TYPE_PORT_ID:
+ if (length < 2 || length > 256) { /* includes the port subtype, hence one extra byte */
+ log_lldp("Port ID field size out of range, ignoring datagram.");
+ return -EBADMSG;
+ }
+ if (n->id.port_id) {
+ log_lldp("Duplicate port ID field, ignoring datagram.");
+ return -EBADMSG;
+ }
+
+ n->id.port_id = memdup(p, length);
+ if (!n->id.port_id)
+ return -ENOMEM;
+
+ n->id.port_id_size = length;
+ break;
+
+ case SD_LLDP_TYPE_TTL:
+ if (length != 2) {
+ log_lldp("TTL field has wrong size, ignoring datagram.");
+ return -EBADMSG;
+ }
+
+ if (n->has_ttl) {
+ log_lldp("Duplicate TTL field, ignoring datagram.");
+ return -EBADMSG;
+ }
+
+ n->ttl = unaligned_read_be16(p);
+ n->has_ttl = true;
+ break;
+
+ case SD_LLDP_TYPE_PORT_DESCRIPTION:
+ r = parse_string(&n->port_description, p, length);
+ if (r < 0)
+ return r;
+ break;
+
+ case SD_LLDP_TYPE_SYSTEM_NAME:
+ r = parse_string(&n->system_name, p, length);
+ if (r < 0)
+ return r;
+ break;
+
+ case SD_LLDP_TYPE_SYSTEM_DESCRIPTION:
+ r = parse_string(&n->system_description, p, length);
+ if (r < 0)
+ return r;
+ break;
+
+ case SD_LLDP_TYPE_SYSTEM_CAPABILITIES:
+ if (length != 4)
+ log_lldp("System capabilities field has wrong size, ignoring.");
+ else {
+ n->system_capabilities = unaligned_read_be16(p);
+ n->enabled_capabilities = unaligned_read_be16(p + 2);
+ n->has_capabilities = true;
+ }
+
+ break;
+
+ case SD_LLDP_TYPE_PRIVATE:
+ if (length < 4)
+ log_lldp("Found private TLV that is too short, ignoring.");
+
+ break;
+ }
+
+
+ p += length, left -= length;
+ }
+
+end_marker:
+ if (!n->id.chassis_id || !n->id.port_id || !n->has_ttl) {
+ log_lldp("One or more mandatory TLV missing in datagram. Ignoring.");
+ return -EBADMSG;
+
+ }
+
+ n->rindex = sizeof(struct ether_header);
+
+ return 0;
+}
+
+void lldp_neighbor_start_ttl(sd_lldp_neighbor *n) {
+ assert(n);
+
+ if (n->ttl > 0) {
+ usec_t base;
+
+ /* Use the packet's timestamp if there is one known */
+ base = triple_timestamp_by_clock(&n->timestamp, clock_boottime_or_monotonic());
+ if (base <= 0 || base == USEC_INFINITY)
+ base = now(clock_boottime_or_monotonic()); /* Otherwise, take the current time */
+
+ n->until = usec_add(base, n->ttl * USEC_PER_SEC);
+ } else
+ n->until = 0;
+
+ if (n->lldp)
+ prioq_reshuffle(n->lldp->neighbor_by_expiry, n, &n->prioq_idx);
+}
+
+bool lldp_neighbor_equal(const sd_lldp_neighbor *a, const sd_lldp_neighbor *b) {
+ if (a == b)
+ return true;
+
+ if (!a || !b)
+ return false;
+
+ if (a->raw_size != b->raw_size)
+ return false;
+
+ return memcmp(LLDP_NEIGHBOR_RAW(a), LLDP_NEIGHBOR_RAW(b), a->raw_size) == 0;
+}
+
+_public_ int sd_lldp_neighbor_get_source_address(sd_lldp_neighbor *n, struct ether_addr* address) {
+ assert_return(n, -EINVAL);
+ assert_return(address, -EINVAL);
+
+ *address = n->source_address;
+ return 0;
+}
+
+_public_ int sd_lldp_neighbor_get_destination_address(sd_lldp_neighbor *n, struct ether_addr* address) {
+ assert_return(n, -EINVAL);
+ assert_return(address, -EINVAL);
+
+ *address = n->destination_address;
+ return 0;
+}
+
+_public_ int sd_lldp_neighbor_get_raw(sd_lldp_neighbor *n, const void **ret, size_t *size) {
+ assert_return(n, -EINVAL);
+ assert_return(ret, -EINVAL);
+ assert_return(size, -EINVAL);
+
+ *ret = LLDP_NEIGHBOR_RAW(n);
+ *size = n->raw_size;
+
+ return 0;
+}
+
+_public_ int sd_lldp_neighbor_get_chassis_id(sd_lldp_neighbor *n, uint8_t *type, const void **ret, size_t *size) {
+ assert_return(n, -EINVAL);
+ assert_return(type, -EINVAL);
+ assert_return(ret, -EINVAL);
+ assert_return(size, -EINVAL);
+
+ assert(n->id.chassis_id_size > 0);
+
+ *type = *(uint8_t*) n->id.chassis_id;
+ *ret = (uint8_t*) n->id.chassis_id + 1;
+ *size = n->id.chassis_id_size - 1;
+
+ return 0;
+}
+
+static int format_mac_address(const void *data, size_t sz, char **ret) {
+ struct ether_addr a;
+ char *k;
+
+ assert(data || sz <= 0);
+
+ if (sz != 7)
+ return 0;
+
+ memcpy(&a, (uint8_t*) data + 1, sizeof(a));
+
+ k = new(char, ETHER_ADDR_TO_STRING_MAX);
+ if (!k)
+ return -ENOMEM;
+
+ *ret = ether_addr_to_string(&a, k);
+ return 1;
+}
+
+static int format_network_address(const void *data, size_t sz, char **ret) {
+ union in_addr_union a;
+ int family, r;
+
+ if (sz == 6 && ((uint8_t*) data)[1] == 1) {
+ memcpy(&a.in, (uint8_t*) data + 2, sizeof(a.in));
+ family = AF_INET;
+ } else if (sz == 18 && ((uint8_t*) data)[1] == 2) {
+ memcpy(&a.in6, (uint8_t*) data + 2, sizeof(a.in6));
+ family = AF_INET6;
+ } else
+ return 0;
+
+ r = in_addr_to_string(family, &a, ret);
+ if (r < 0)
+ return r;
+ return 1;
+}
+
+_public_ int sd_lldp_neighbor_get_chassis_id_as_string(sd_lldp_neighbor *n, const char **ret) {
+ char *k;
+ int r;
+
+ assert_return(n, -EINVAL);
+ assert_return(ret, -EINVAL);
+
+ if (n->chassis_id_as_string) {
+ *ret = n->chassis_id_as_string;
+ return 0;
+ }
+
+ assert(n->id.chassis_id_size > 0);
+
+ switch (*(uint8_t*) n->id.chassis_id) {
+
+ case SD_LLDP_CHASSIS_SUBTYPE_CHASSIS_COMPONENT:
+ case SD_LLDP_CHASSIS_SUBTYPE_INTERFACE_ALIAS:
+ case SD_LLDP_CHASSIS_SUBTYPE_PORT_COMPONENT:
+ case SD_LLDP_CHASSIS_SUBTYPE_INTERFACE_NAME:
+ case SD_LLDP_CHASSIS_SUBTYPE_LOCALLY_ASSIGNED:
+ k = cescape_length((char*) n->id.chassis_id + 1, n->id.chassis_id_size - 1);
+ if (!k)
+ return -ENOMEM;
+
+ goto done;
+
+ case SD_LLDP_CHASSIS_SUBTYPE_MAC_ADDRESS:
+ r = format_mac_address(n->id.chassis_id, n->id.chassis_id_size, &k);
+ if (r < 0)
+ return r;
+ if (r > 0)
+ goto done;
+
+ break;
+
+ case SD_LLDP_CHASSIS_SUBTYPE_NETWORK_ADDRESS:
+ r = format_network_address(n->id.chassis_id, n->id.chassis_id_size, &k);
+ if (r < 0)
+ return r;
+ if (r > 0)
+ goto done;
+
+ break;
+ }
+
+ /* Generic fallback */
+ k = hexmem(n->id.chassis_id, n->id.chassis_id_size);
+ if (!k)
+ return -ENOMEM;
+
+done:
+ *ret = n->chassis_id_as_string = k;
+ return 0;
+}
+
+_public_ int sd_lldp_neighbor_get_port_id(sd_lldp_neighbor *n, uint8_t *type, const void **ret, size_t *size) {
+ assert_return(n, -EINVAL);
+ assert_return(type, -EINVAL);
+ assert_return(ret, -EINVAL);
+ assert_return(size, -EINVAL);
+
+ assert(n->id.port_id_size > 0);
+
+ *type = *(uint8_t*) n->id.port_id;
+ *ret = (uint8_t*) n->id.port_id + 1;
+ *size = n->id.port_id_size - 1;
+
+ return 0;
+}
+
+_public_ int sd_lldp_neighbor_get_port_id_as_string(sd_lldp_neighbor *n, const char **ret) {
+ char *k;
+ int r;
+
+ assert_return(n, -EINVAL);
+ assert_return(ret, -EINVAL);
+
+ if (n->port_id_as_string) {
+ *ret = n->port_id_as_string;
+ return 0;
+ }
+
+ assert(n->id.port_id_size > 0);
+
+ switch (*(uint8_t*) n->id.port_id) {
+
+ case SD_LLDP_PORT_SUBTYPE_INTERFACE_ALIAS:
+ case SD_LLDP_PORT_SUBTYPE_PORT_COMPONENT:
+ case SD_LLDP_PORT_SUBTYPE_INTERFACE_NAME:
+ case SD_LLDP_PORT_SUBTYPE_LOCALLY_ASSIGNED:
+ k = cescape_length((char*) n->id.port_id + 1, n->id.port_id_size - 1);
+ if (!k)
+ return -ENOMEM;
+
+ goto done;
+
+ case SD_LLDP_PORT_SUBTYPE_MAC_ADDRESS:
+ r = format_mac_address(n->id.port_id, n->id.port_id_size, &k);
+ if (r < 0)
+ return r;
+ if (r > 0)
+ goto done;
+
+ break;
+
+ case SD_LLDP_PORT_SUBTYPE_NETWORK_ADDRESS:
+ r = format_network_address(n->id.port_id, n->id.port_id_size, &k);
+ if (r < 0)
+ return r;
+ if (r > 0)
+ goto done;
+
+ break;
+ }
+
+ /* Generic fallback */
+ k = hexmem(n->id.port_id, n->id.port_id_size);
+ if (!k)
+ return -ENOMEM;
+
+done:
+ *ret = n->port_id_as_string = k;
+ return 0;
+}
+
+_public_ int sd_lldp_neighbor_get_ttl(sd_lldp_neighbor *n, uint16_t *ret_sec) {
+ assert_return(n, -EINVAL);
+ assert_return(ret_sec, -EINVAL);
+
+ *ret_sec = n->ttl;
+ return 0;
+}
+
+_public_ int sd_lldp_neighbor_get_system_name(sd_lldp_neighbor *n, const char **ret) {
+ assert_return(n, -EINVAL);
+ assert_return(ret, -EINVAL);
+
+ if (!n->system_name)
+ return -ENODATA;
+
+ *ret = n->system_name;
+ return 0;
+}
+
+_public_ int sd_lldp_neighbor_get_system_description(sd_lldp_neighbor *n, const char **ret) {
+ assert_return(n, -EINVAL);
+ assert_return(ret, -EINVAL);
+
+ if (!n->system_description)
+ return -ENODATA;
+
+ *ret = n->system_description;
+ return 0;
+}
+
+_public_ int sd_lldp_neighbor_get_port_description(sd_lldp_neighbor *n, const char **ret) {
+ assert_return(n, -EINVAL);
+ assert_return(ret, -EINVAL);
+
+ if (!n->port_description)
+ return -ENODATA;
+
+ *ret = n->port_description;
+ return 0;
+}
+
+_public_ int sd_lldp_neighbor_get_system_capabilities(sd_lldp_neighbor *n, uint16_t *ret) {
+ assert_return(n, -EINVAL);
+ assert_return(ret, -EINVAL);
+
+ if (!n->has_capabilities)
+ return -ENODATA;
+
+ *ret = n->system_capabilities;
+ return 0;
+}
+
+_public_ int sd_lldp_neighbor_get_enabled_capabilities(sd_lldp_neighbor *n, uint16_t *ret) {
+ assert_return(n, -EINVAL);
+ assert_return(ret, -EINVAL);
+
+ if (!n->has_capabilities)
+ return -ENODATA;
+
+ *ret = n->enabled_capabilities;
+ return 0;
+}
+
+_public_ int sd_lldp_neighbor_from_raw(sd_lldp_neighbor **ret, const void *raw, size_t raw_size) {
+ _cleanup_(sd_lldp_neighbor_unrefp) sd_lldp_neighbor *n = NULL;
+ int r;
+
+ assert_return(ret, -EINVAL);
+ assert_return(raw || raw_size <= 0, -EINVAL);
+
+ n = lldp_neighbor_new(raw_size);
+ if (!n)
+ return -ENOMEM;
+
+ memcpy(LLDP_NEIGHBOR_RAW(n), raw, raw_size);
+ r = lldp_neighbor_parse(n);
+ if (r < 0)
+ return r;
+
+ *ret = n;
+ n = NULL;
+
+ return r;
+}
+
+_public_ int sd_lldp_neighbor_tlv_rewind(sd_lldp_neighbor *n) {
+ assert_return(n, -EINVAL);
+
+ assert(n->raw_size >= sizeof(struct ether_header));
+ n->rindex = sizeof(struct ether_header);
+
+ return n->rindex < n->raw_size;
+}
+
+_public_ int sd_lldp_neighbor_tlv_next(sd_lldp_neighbor *n) {
+ size_t length;
+
+ assert_return(n, -EINVAL);
+
+ if (n->rindex == n->raw_size) /* EOF */
+ return -ESPIPE;
+
+ if (n->rindex + 2 > n->raw_size) /* Truncated message */
+ return -EBADMSG;
+
+ length = LLDP_NEIGHBOR_TLV_LENGTH(n);
+ if (n->rindex + 2 + length > n->raw_size)
+ return -EBADMSG;
+
+ n->rindex += 2 + length;
+ return n->rindex < n->raw_size;
+}
+
+_public_ int sd_lldp_neighbor_tlv_get_type(sd_lldp_neighbor *n, uint8_t *type) {
+ assert_return(n, -EINVAL);
+ assert_return(type, -EINVAL);
+
+ if (n->rindex == n->raw_size) /* EOF */
+ return -ESPIPE;
+
+ if (n->rindex + 2 > n->raw_size)
+ return -EBADMSG;
+
+ *type = LLDP_NEIGHBOR_TLV_TYPE(n);
+ return 0;
+}
+
+_public_ int sd_lldp_neighbor_tlv_is_type(sd_lldp_neighbor *n, uint8_t type) {
+ uint8_t k;
+ int r;
+
+ assert_return(n, -EINVAL);
+
+ r = sd_lldp_neighbor_tlv_get_type(n, &k);
+ if (r < 0)
+ return r;
+
+ return type == k;
+}
+
+_public_ int sd_lldp_neighbor_tlv_get_oui(sd_lldp_neighbor *n, uint8_t oui[3], uint8_t *subtype) {
+ const uint8_t *d;
+ size_t length;
+ int r;
+
+ assert_return(n, -EINVAL);
+ assert_return(oui, -EINVAL);
+ assert_return(subtype, -EINVAL);
+
+ r = sd_lldp_neighbor_tlv_is_type(n, SD_LLDP_TYPE_PRIVATE);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return -ENXIO;
+
+ length = LLDP_NEIGHBOR_TLV_LENGTH(n);
+ if (length < 4)
+ return -EBADMSG;
+
+ if (n->rindex + 2 + length > n->raw_size)
+ return -EBADMSG;
+
+ d = LLDP_NEIGHBOR_TLV_DATA(n);
+ memcpy(oui, d, 3);
+ *subtype = d[3];
+
+ return 0;
+}
+
+_public_ int sd_lldp_neighbor_tlv_is_oui(sd_lldp_neighbor *n, const uint8_t oui[3], uint8_t subtype) {
+ uint8_t k[3], st;
+ int r;
+
+ r = sd_lldp_neighbor_tlv_get_oui(n, k, &st);
+ if (r == -ENXIO)
+ return 0;
+ if (r < 0)
+ return r;
+
+ return memcmp(k, oui, 3) == 0 && st == subtype;
+}
+
+_public_ int sd_lldp_neighbor_tlv_get_raw(sd_lldp_neighbor *n, const void **ret, size_t *size) {
+ size_t length;
+
+ assert_return(n, -EINVAL);
+ assert_return(ret, -EINVAL);
+ assert_return(size, -EINVAL);
+
+ /* Note that this returns the full TLV, including the TLV header */
+
+ if (n->rindex + 2 > n->raw_size)
+ return -EBADMSG;
+
+ length = LLDP_NEIGHBOR_TLV_LENGTH(n);
+ if (n->rindex + 2 + length > n->raw_size)
+ return -EBADMSG;
+
+ *ret = (uint8_t*) LLDP_NEIGHBOR_RAW(n) + n->rindex;
+ *size = length + 2;
+
+ return 0;
+}
+
+_public_ int sd_lldp_neighbor_get_timestamp(sd_lldp_neighbor *n, clockid_t clock, uint64_t *ret) {
+ assert_return(n, -EINVAL);
+ assert_return(TRIPLE_TIMESTAMP_HAS_CLOCK(clock), -EOPNOTSUPP);
+ assert_return(clock_supported(clock), -EOPNOTSUPP);
+ assert_return(ret, -EINVAL);
+
+ if (!triple_timestamp_is_set(&n->timestamp))
+ return -ENODATA;
+
+ *ret = triple_timestamp_by_clock(&n->timestamp, clock);
+ return 0;
+}
diff --git a/src/libsystemd-network/lldp-neighbor.h b/src/libsystemd-network/lldp-neighbor.h
new file mode 100644
index 0000000000..c1a7606d06
--- /dev/null
+++ b/src/libsystemd-network/lldp-neighbor.h
@@ -0,0 +1,108 @@
+#pragma once
+
+/***
+ This file is part of systemd.
+
+ Copyright 2016 Lennart Poettering
+
+ 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 <inttypes.h>
+#include <stdbool.h>
+#include <sys/types.h>
+
+#include "sd-lldp.h"
+
+#include "hash-funcs.h"
+#include "lldp-internal.h"
+#include "time-util.h"
+
+typedef struct LLDPNeighborID {
+ /* The spec calls this an "MSAP identifier" */
+ void *chassis_id;
+ size_t chassis_id_size;
+
+ void *port_id;
+ size_t port_id_size;
+} LLDPNeighborID;
+
+struct sd_lldp_neighbor {
+ /* Neighbor objects stay around as long as they are linked into an "sd_lldp" object or n_ref > 0. */
+ sd_lldp *lldp;
+ unsigned n_ref;
+
+ triple_timestamp timestamp;
+
+ usec_t until;
+ unsigned prioq_idx;
+
+ struct ether_addr source_address;
+ struct ether_addr destination_address;
+
+ LLDPNeighborID id;
+
+ /* The raw packet size. The data is appended to the object, accessible via LLDP_NEIGHBOR_RAW() */
+ size_t raw_size;
+
+ /* The current read index for the iterative TLV interface */
+ size_t rindex;
+
+ /* And a couple of fields parsed out. */
+ bool has_ttl:1;
+ bool has_capabilities:1;
+ bool has_port_vlan_id:1;
+
+ uint16_t ttl;
+
+ uint16_t system_capabilities;
+ uint16_t enabled_capabilities;
+
+ char *port_description;
+ char *system_name;
+ char *system_description;
+
+ uint16_t port_vlan_id;
+
+ char *chassis_id_as_string;
+ char *port_id_as_string;
+};
+
+static inline void *LLDP_NEIGHBOR_RAW(const sd_lldp_neighbor *n) {
+ return (uint8_t*) n + ALIGN(sizeof(sd_lldp_neighbor));
+}
+
+static inline uint8_t LLDP_NEIGHBOR_TLV_TYPE(const sd_lldp_neighbor *n) {
+ return ((uint8_t*) LLDP_NEIGHBOR_RAW(n))[n->rindex] >> 1;
+}
+
+static inline size_t LLDP_NEIGHBOR_TLV_LENGTH(const sd_lldp_neighbor *n) {
+ uint8_t *p;
+
+ p = (uint8_t*) LLDP_NEIGHBOR_RAW(n) + n->rindex;
+ return p[1] + (((size_t) (p[0] & 1)) << 8);
+}
+
+static inline void* LLDP_NEIGHBOR_TLV_DATA(const sd_lldp_neighbor *n) {
+ return ((uint8_t*) LLDP_NEIGHBOR_RAW(n)) + n->rindex + 2;
+}
+
+extern const struct hash_ops lldp_neighbor_id_hash_ops;
+int lldp_neighbor_prioq_compare_func(const void *a, const void *b);
+
+sd_lldp_neighbor *lldp_neighbor_unlink(sd_lldp_neighbor *n);
+sd_lldp_neighbor *lldp_neighbor_new(size_t raw_size);
+int lldp_neighbor_parse(sd_lldp_neighbor *n);
+void lldp_neighbor_start_ttl(sd_lldp_neighbor *n);
+bool lldp_neighbor_equal(const sd_lldp_neighbor *a, const sd_lldp_neighbor *b);
diff --git a/src/libsystemd-network/lldp-network.c b/src/libsystemd-network/lldp-network.c
index 664d2f7867..59c25598e9 100644
--- a/src/libsystemd-network/lldp-network.c
+++ b/src/libsystemd-network/lldp-network.c
@@ -1,5 +1,3 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
/***
This file is part of systemd.
@@ -21,91 +19,59 @@
***/
#include <linux/filter.h>
-#include <linux/if_ether.h>
+#include <netinet/if_ether.h>
-#include "socket-util.h"
-#include "lldp-tlv.h"
+#include "fd-util.h"
#include "lldp-network.h"
-#include "lldp-internal.h"
+#include "socket-util.h"
int lldp_network_bind_raw_socket(int ifindex) {
- typedef struct LLDPFrame {
- struct ethhdr hdr;
- uint8_t tlvs[0];
- } LLDPFrame;
- struct sock_filter filter[] = {
- BPF_STMT(BPF_LD + BPF_W + BPF_ABS, offsetof(LLDPFrame, hdr.h_dest)), /* A <- 4 bytes of destination MAC */
+ static const struct sock_filter filter[] = {
+ BPF_STMT(BPF_LD + BPF_W + BPF_ABS, offsetof(struct ethhdr, h_dest)), /* A <- 4 bytes of destination MAC */
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0x0180c200, 1, 0), /* A != 01:80:c2:00 */
BPF_STMT(BPF_RET + BPF_K, 0), /* drop packet */
- BPF_STMT(BPF_LD + BPF_H + BPF_ABS, offsetof(LLDPFrame, hdr.h_dest) + 4), /* A <- remaining 2 bytes of destination MAC */
+ BPF_STMT(BPF_LD + BPF_H + BPF_ABS, offsetof(struct ethhdr, h_dest) + 4), /* A <- remaining 2 bytes of destination MAC */
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0x0000, 3, 0), /* A != 00:00 */
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0x0003, 2, 0), /* A != 00:03 */
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0x000e, 1, 0), /* A != 00:0e */
BPF_STMT(BPF_RET + BPF_K, 0), /* drop packet */
- BPF_STMT(BPF_LD + BPF_H + BPF_ABS, offsetof(LLDPFrame, hdr.h_proto)), /* A <- protocol */
+ BPF_STMT(BPF_LD + BPF_H + BPF_ABS, offsetof(struct ethhdr, h_proto)), /* A <- protocol */
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ETHERTYPE_LLDP, 1, 0), /* A != ETHERTYPE_LLDP */
BPF_STMT(BPF_RET + BPF_K, 0), /* drop packet */
BPF_STMT(BPF_RET + BPF_K, (uint32_t) -1), /* accept packet */
};
- struct sock_fprog fprog = {
+ static const struct sock_fprog fprog = {
.len = ELEMENTSOF(filter),
- .filter = filter
+ .filter = (struct sock_filter*) filter,
};
- _cleanup_close_ int s = -1;
-
union sockaddr_union saddrll = {
.ll.sll_family = AF_PACKET,
.ll.sll_ifindex = ifindex,
};
+ _cleanup_close_ int fd = -1;
int r;
assert(ifindex > 0);
- s = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
- if (s < 0)
+ fd = socket(PF_PACKET, SOCK_RAW|SOCK_CLOEXEC|SOCK_NONBLOCK,
+ htobe16(ETHERTYPE_LLDP));
+ if (fd < 0)
return -errno;
- r = setsockopt(s, SOL_SOCKET, SO_ATTACH_FILTER, &fprog, sizeof(fprog));
+ r = setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, &fprog, sizeof(fprog));
if (r < 0)
return -errno;
- r = bind(s, &saddrll.sa, sizeof(saddrll.ll));
+ r = bind(fd, &saddrll.sa, sizeof(saddrll.ll));
if (r < 0)
return -errno;
- r = s;
- s = -1;
+ r = fd;
+ fd = -1;
return r;
}
-
-int lldp_receive_packet(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
- _cleanup_tlv_packet_free_ tlv_packet *packet = NULL;
- tlv_packet *p;
- uint16_t length;
- int r;
-
- assert(fd);
- assert(userdata);
-
- r = tlv_packet_new(&packet);
- if (r < 0)
- return r;
-
- length = read(fd, &packet->pdu, sizeof(packet->pdu));
-
- /* Silently drop the packet */
- if ((size_t) length > ETHER_MAX_LEN)
- return 0;
-
- packet->userdata = userdata;
-
- p = packet;
- packet = NULL;
-
- return lldp_handle_packet(p, (uint16_t) length);
-}
diff --git a/src/libsystemd-network/lldp-network.h b/src/libsystemd-network/lldp-network.h
index b7f8d3bf80..c4cf8c79f1 100644
--- a/src/libsystemd-network/lldp-network.h
+++ b/src/libsystemd-network/lldp-network.h
@@ -1,4 +1,4 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+#pragma once
/***
This file is part of systemd.
@@ -20,9 +20,6 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#pragma once
-
#include "sd-event.h"
int lldp_network_bind_raw_socket(int ifindex);
-int lldp_receive_packet(sd_event_source *s, int fd, uint32_t revents, void *userdata);
diff --git a/src/libsystemd-network/lldp-port.c b/src/libsystemd-network/lldp-port.c
deleted file mode 100644
index aa6a3b9224..0000000000
--- a/src/libsystemd-network/lldp-port.c
+++ /dev/null
@@ -1,116 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- This file is part of systemd.
-
- Copyright (C) 2014 Tom Gundersen
- Copyright (C) 2014 Susant Sahani
-
- 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 "async.h"
-#include "lldp-port.h"
-#include "lldp-network.h"
-
-int lldp_port_start(lldp_port *p) {
- int r;
-
- assert_return(p, -EINVAL);
-
- r = lldp_network_bind_raw_socket(p->ifindex);
- if (r < 0)
- return r;
-
- p->rawfd = r;
-
- r = sd_event_add_io(p->event, &p->lldp_port_rx,
- p->rawfd, EPOLLIN, lldp_receive_packet, p);
- if (r < 0) {
- log_debug("Failed to allocate event source: %s", strerror(-r));
- return r;
- }
-
- r = sd_event_source_set_priority(p->lldp_port_rx, p->event_priority);
- if (r < 0) {
- log_debug("Failed to set event priority: %s", strerror(-r));
- goto fail;
- }
-
- r = sd_event_source_set_description(p->lldp_port_rx, "lldp-port-rx");
- if (r < 0) {
- log_debug("Failed to set event name: %s", strerror(-r));
- goto fail;
- }
-
- return 0;
-
-fail:
- lldp_port_stop(p);
-
- return r;
-}
-
-int lldp_port_stop(lldp_port *p) {
-
- assert_return(p, -EINVAL);
-
- p->rawfd = asynchronous_close(p->rawfd);
- p->lldp_port_rx = sd_event_source_unref(p->lldp_port_rx);
-
- return 0;
-}
-
-void lldp_port_free(lldp_port *p) {
- if (!p)
- return;
-
- lldp_port_stop(p);
-
- free(p->ifname);
- free(p);
-}
-
-int lldp_port_new(int ifindex,
- const char *ifname,
- const struct ether_addr *addr,
- void *userdata,
- lldp_port **ret) {
- _cleanup_free_ lldp_port *p = NULL;
-
- assert_return(ifindex, -EINVAL);
- assert_return(ifname, -EINVAL);
- assert_return(addr, -EINVAL);
-
- p = new0(lldp_port, 1);
- if (!p)
- return -ENOMEM;
-
- p->rawfd = -1;
- p->ifindex = ifindex;
-
- p->ifname = strdup(ifname);
- if (!p->ifname)
- return -ENOMEM;
-
- memcpy(&p->mac, addr, ETH_ALEN);
-
- p->userdata = userdata;
-
- *ret = p;
-
- p = NULL;
-
- return 0;
-}
diff --git a/src/libsystemd-network/lldp-port.h b/src/libsystemd-network/lldp-port.h
deleted file mode 100644
index b2d3180091..0000000000
--- a/src/libsystemd-network/lldp-port.h
+++ /dev/null
@@ -1,63 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- This file is part of systemd.
-
- Copyright (C) 2014 Tom Gundersen
- Copyright (C) 2014 Susant Sahani
-
- 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/>.
-***/
-
-#pragma once
-
-#include <net/ethernet.h>
-
-#include "sd-event.h"
-#include "sd-lldp.h"
-
-#include "util.h"
-
-typedef struct lldp_port lldp_port;
-
-struct lldp_port {
- LLDPPortStatus status;
-
- int ifindex;
- char *ifname;
-
- struct ether_addr mac;
-
- int rawfd;
-
- sd_event *event;
- sd_event_source *lldp_port_rx;
-
- int event_priority;
-
- void *userdata;
-};
-
-int lldp_port_new(int ifindex,
- const char *ifname,
- const struct ether_addr *addr,
- void *userdata,
- lldp_port **ret);
-void lldp_port_free(lldp_port *p);
-
-DEFINE_TRIVIAL_CLEANUP_FUNC(lldp_port*, lldp_port_free);
-#define _cleanup_lldp_port_free_ _cleanup_(lldp_port_freep)
-
-int lldp_port_start(lldp_port *p);
-int lldp_port_stop(lldp_port *p);
diff --git a/src/libsystemd-network/lldp-tlv.c b/src/libsystemd-network/lldp-tlv.c
deleted file mode 100644
index 0cea5b10a6..0000000000
--- a/src/libsystemd-network/lldp-tlv.c
+++ /dev/null
@@ -1,321 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- This file is part of systemd.
-
- Copyright (C) 2014 Tom Gundersen
- Copyright (C) 2014 Susant Sahani
-
- 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 <net/ethernet.h>
-#include <arpa/inet.h>
-
-#include "macro.h"
-#include "lldp-tlv.h"
-
-int tlv_section_new(tlv_section **ret) {
- tlv_section *s;
-
- s = new0(tlv_section, 1);
- if (!s)
- return -ENOMEM;
-
- *ret = s;
-
- return 0;
-}
-
-void tlv_section_free(tlv_section *m) {
-
- if (!m)
- return;
-
- free(m);
-}
-
-int tlv_packet_new(tlv_packet **ret) {
- tlv_packet *m;
-
- m = new0(tlv_packet, 1);
- if (!m)
- return -ENOMEM;
-
- LIST_HEAD_INIT(m->sections);
-
- *ret = m;
-
- return 0;
-}
-
-void tlv_packet_free(tlv_packet *m) {
- tlv_section *s, *n;
-
- if (!m)
- return;
-
- LIST_FOREACH_SAFE(section, s, n, m->sections)
- tlv_section_free(s);
-
- free(m);
-}
-
-int tlv_packet_append_bytes(tlv_packet *m, const void *data, size_t data_length) {
- uint8_t *p;
-
- assert_return(m, -EINVAL);
- assert_return(data, -EINVAL);
- assert_return(data_length, -EINVAL);
-
- if (m->length + data_length > ETHER_MAX_LEN)
- return -ENOMEM;
-
- p = m->pdu + m->length;
- memcpy(p, data, data_length);
- m->length += data_length;
-
- return 0;
-}
-
-int tlv_packet_append_u8(tlv_packet *m, uint8_t data) {
-
- assert_return(m, -EINVAL);
-
- return tlv_packet_append_bytes(m, &data, sizeof(uint8_t));
-}
-
-int tlv_packet_append_u16(tlv_packet *m, uint16_t data) {
- uint16_t type;
-
- assert_return(m, -EINVAL);
-
- type = htons(data);
-
- return tlv_packet_append_bytes(m, &type, sizeof(uint16_t));
-}
-
-int tlv_packet_append_u32(tlv_packet *m, uint32_t data) {
- uint32_t type;
-
- assert_return(m, -EINVAL);
-
- type = htonl(data);
-
- return tlv_packet_append_bytes(m, &type, sizeof(uint32_t));
-}
-
-int tlv_packet_append_string(tlv_packet *m, char *data, uint16_t size) {
-
- assert_return(m, -EINVAL);
-
- return tlv_packet_append_bytes(m, data, size);
-}
-
-int lldp_tlv_packet_open_container(tlv_packet *m, uint16_t type) {
-
- assert_return(m, -EINVAL);
-
- m->container_pos = m->pdu + m->length;
-
- return tlv_packet_append_u16(m, type << 9);
-}
-
-int lldp_tlv_packet_close_container(tlv_packet *m) {
- uint16_t type;
-
- assert_return(m, -EINVAL);
- assert_return(m->container_pos, -EINVAL);
-
- memcpy(&type, m->container_pos, sizeof(uint16_t));
-
- type |= htons(((m->pdu + m->length) - (m->container_pos + 2)) & 0x01ff);
- memcpy(m->container_pos, &type, sizeof(uint16_t));
-
- return 0;
-}
-
-static inline int tlv_packet_read_internal(tlv_section *m, void **data) {
-
- assert_return(m->read_pos, -EINVAL);
-
- *data = m->read_pos;
-
- return 0;
-}
-
-int tlv_packet_read_u8(tlv_packet *m, uint8_t *data) {
- void *val = NULL;
- int r;
-
- assert_return(m, -EINVAL);
-
- r = tlv_packet_read_internal(m->container, &val);
- if (r < 0)
- return r;
-
- memcpy(data, val, sizeof(uint8_t));
-
- m->container->read_pos ++;
-
- return 0;
-}
-
-int tlv_packet_read_u16(tlv_packet *m, uint16_t *data) {
- uint16_t t;
- void *val = NULL;
- int r;
-
- assert_return(m, -EINVAL);
-
- r = tlv_packet_read_internal(m->container, &val);
- if (r < 0)
- return r;
-
- memcpy(&t, val, sizeof(uint16_t));
- *data = ntohs(t);
-
- m->container->read_pos += 2;
-
- return 0;
-}
-
-int tlv_packet_read_u32(tlv_packet *m, uint32_t *data) {
- uint32_t t;
- void *val;
- int r;
-
- assert_return(m, -EINVAL);
-
- r = tlv_packet_read_internal(m->container, &val);
- if (r < 0)
- return r;
-
- memcpy(&t, val, sizeof(uint32_t));
- *data = ntohl(t);
-
- m->container->read_pos += 4;
-
- return r;
-}
-
-int tlv_packet_read_string(tlv_packet *m, char **data, uint16_t *data_length) {
- void *val = NULL;
- int r;
-
- assert_return(m, -EINVAL);
-
- r = tlv_packet_read_internal(m->container, &val);
- if (r < 0)
- return r;
-
- *data = (char *) val;
- *data_length = m->container->length;
-
- m->container->read_pos += m->container->length;
-
- return 0;
-}
-
-int tlv_packet_read_bytes(tlv_packet *m, uint8_t **data, uint16_t *data_length) {
- void *val = NULL;
- int r;
-
- assert_return(m, -EINVAL);
-
- r = tlv_packet_read_internal(m->container, &val);
- if (r < 0)
- return r;
-
- *data = (uint8_t *) val;
- *data_length = m->container->length;
-
- m->container->read_pos += m->container->length;
-
- return 0;
-}
-
-/* parse raw TLV packet */
-int tlv_packet_parse_pdu(tlv_packet *m, uint16_t size) {
- tlv_section *section, *tail;
- uint16_t t, l;
- uint8_t *p;
- int r;
-
- assert_return(m, -EINVAL);
- assert_return(size, -EINVAL);
-
- p = m->pdu;
-
- /* extract ethernet herader */
- memcpy(&m->mac, p, ETH_ALEN);
- p += sizeof(struct ether_header);
-
- for (l = 0; l <= size; ) {
- r = tlv_section_new(&section);
- if (r < 0)
- return r;
-
- memcpy(&t, p, sizeof(uint16_t));
-
- section->type = ntohs(t) >> 9;
- section->length = ntohs(t) & 0x01ff;
-
- if (section->type == LLDP_TYPE_END || section->type >=_LLDP_TYPE_MAX) {
- tlv_section_free(section);
- break;
- }
-
- p += 2;
- section->data = p;
-
- LIST_FIND_TAIL(section, m->sections, tail);
- LIST_INSERT_AFTER(section, m->sections, tail, section);
-
- p += section->length;
- l += (section->length + 2);
- }
-
- return 0;
-}
-
-int lldp_tlv_packet_enter_container(tlv_packet *m, uint16_t type) {
- tlv_section *s;
-
- assert_return(m, -EINVAL);
-
- LIST_FOREACH(section, s, m->sections)
- if (s->type == type)
- break;
- if (!s)
- return -1;
-
- m->container = s;
-
- m->container->read_pos = s->data;
- if (!m->container->read_pos) {
- m->container = 0;
- return -1;
- }
-
- return 0;
-}
-
-int lldp_tlv_packet_exit_container(tlv_packet *m) {
- assert_return(m, -EINVAL);
-
- m->container = 0;
-
- return 0;
-}
diff --git a/src/libsystemd-network/lldp-tlv.h b/src/libsystemd-network/lldp-tlv.h
deleted file mode 100644
index ce3334e115..0000000000
--- a/src/libsystemd-network/lldp-tlv.h
+++ /dev/null
@@ -1,87 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- This file is part of systemd.
-
- Copyright (C) 2014 Tom Gundersen
- Copyright (C) 2014 Susant Sahani
-
- 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/>.
-***/
-
-#pragma once
-
-#include <net/ethernet.h>
-
-#include "util.h"
-#include "lldp.h"
-#include "list.h"
-
-typedef struct tlv_packet tlv_packet;
-typedef struct tlv_section tlv_section;
-
-struct tlv_section {
- uint16_t type;
- uint16_t length;
-
- uint8_t *read_pos;
- uint8_t *data;
-
- LIST_FIELDS(tlv_section, section);
-};
-
-int tlv_section_new(tlv_section **ret);
-void tlv_section_free(tlv_section *ret);
-
-struct tlv_packet {
- uint16_t type;
- uint16_t length;
- usec_t ts;
-
- uint8_t *container_pos;
- uint8_t pdu[ETHER_MAX_LEN];
-
- void *userdata;
-
- struct ether_addr mac;
- tlv_section *container;
-
- LIST_HEAD(tlv_section, sections);
-};
-
-int tlv_packet_new(tlv_packet **ret);
-void tlv_packet_free(tlv_packet *m);
-
-DEFINE_TRIVIAL_CLEANUP_FUNC(tlv_packet*, tlv_packet_free);
-#define _cleanup_tlv_packet_free_ _cleanup_(tlv_packet_freep)
-
-int lldp_tlv_packet_open_container(tlv_packet *m, uint16_t type);
-int lldp_tlv_packet_close_container(tlv_packet *m);
-
-int tlv_packet_append_bytes(tlv_packet *m, const void *data, size_t data_length);
-int tlv_packet_append_u8(tlv_packet *m, uint8_t data);
-int tlv_packet_append_u16(tlv_packet *m, uint16_t data);
-int tlv_packet_append_u32(tlv_packet *m, uint32_t data);
-int tlv_packet_append_string(tlv_packet *m, char *data, uint16_t size);
-
-int lldp_tlv_packet_enter_container(tlv_packet *m, uint16_t type);
-int lldp_tlv_packet_exit_container(tlv_packet *m);
-
-int tlv_packet_read_bytes(tlv_packet *m, uint8_t **data, uint16_t *data_length);
-int tlv_packet_read_string(tlv_packet *m, char **data, uint16_t *data_length);
-int tlv_packet_read_u8(tlv_packet *m, uint8_t *data);
-int tlv_packet_read_u16(tlv_packet *m, uint16_t *data);
-int tlv_packet_read_u32(tlv_packet *m, uint32_t *data);
-
-int tlv_packet_parse_pdu(tlv_packet *t, uint16_t size);
diff --git a/src/libsystemd-network/lldp.h b/src/libsystemd-network/lldp.h
deleted file mode 100644
index 5e4b283e26..0000000000
--- a/src/libsystemd-network/lldp.h
+++ /dev/null
@@ -1,115 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- This file is part of systemd.
-
- Copyright (C) 2014 Tom Gundersen
- Copyright (C) 2014 Susant Sahani
-
- 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/>.
-***/
-
-#pragma once
-
-#define LLDP_MULTICAST_ADDR { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x0e }
-
-#define ETHERTYPE_LLDP 0x88cc
-
-/* IEEE 802.3AB Clause 9: TLV Types */
-typedef enum LLDPTypes {
- LLDP_TYPE_END = 0,
- LLDP_TYPE_CHASSIS_ID = 1,
- LLDP_TYPE_PORT_ID = 2,
- LLDP_TYPE_TTL = 3,
- LLDP_TYPE_PORT_DESCRIPTION = 4,
- LLDP_TYPE_SYSTEM_NAME = 5,
- LLDP_TYPE_SYSTEM_DESCRIPTION = 6,
- LLDP_TYPE_SYSTEM_CAPABILITIES = 7,
- LLDP_TYPE_MGMT_ADDRESS = 8,
- LLDP_TYPE_PRIVATE = 127,
- _LLDP_TYPE_MAX,
- _LLDP_TYPE_INVALID = -1,
-} LLDPTypes;
-
-/* IEEE 802.3AB Clause 9.5.2: Chassis subtypes */
-typedef enum LLDPChassisSubtypes {
- LLDP_CHASSIS_SUBTYPE_RESERVED = 0,
- LLDP_CHASSIS_SUBTYPE_CHASSIS_COMPONENT = 1,
- LLDP_CHASSIS_SUBTYPE_INTERFACE_ALIAS = 2,
- LLDP_CHASSIS_SUBTYPE_PORT_COMPONENT = 3,
- LLDP_CHASSIS_SUBTYPE_MAC_ADDRESS = 4,
- LLDP_CHASSIS_SUBTYPE_NETWORK_ADDRESS = 5,
- LLDP_CHASSIS_SUBTYPE_INTERFACE_NAME = 6,
- LLDP_CHASSIS_SUBTYPE_LOCALLY_ASSIGNED = 7,
- _LLDP_CHASSIS_SUBTYPE_MAX,
- _LLDP_CHASSIS_SUBTYPE_INVALID = -1,
-} LLDPChassisSubtypes;
-
-/* IEEE 802.3AB Clause 9.5.3: Port subtype */
-typedef enum LLDPPortSubtypes {
- LLDP_PORT_SUBTYPE_RESERVED = 0,
- LLDP_PORT_SUBTYPE_INTERFACE_ALIAS = 1,
- LLDP_PORT_SUBTYPE_PORT_COMPONENT = 2,
- LLDP_PORT_SUBTYPE_MAC_ADDRESS = 3,
- LLDP_PORT_SUBTYPE_NETWORK = 4,
- LLDP_PORT_SUBTYPE_INTERFACE_NAME = 5,
- LLDP_PORT_SUBTYPE_AGENT_CIRCUIT_ID = 6,
- LLDP_PORT_SUBTYPE_LOCALLY_ASSIGNED = 7,
- _LLDP_PORT_SUBTYPE_MAX,
- _LLDP_PORT_SUBTYPE_INVALID = -1
-} LLDPPortSubtypes;
-
-typedef enum LLDPSystemCapabilities {
- LLDP_SYSTEM_CAPABILITIES_OTHER = 1 << 0,
- LLDP_SYSTEM_CAPABILITIES_REPEATER = 1 << 1,
- LLDP_SYSTEM_CAPABILITIES_BRIDGE = 1 << 2,
- LLDP_SYSTEM_CAPABILITIES_WLAN_AP = 1 << 3,
- LLDP_SYSTEM_CAPABILITIES_ROUTER = 1 << 4,
- LLDP_SYSTEM_CAPABILITIES_PHONE = 1 << 5,
- LLDP_SYSTEM_CAPABILITIES_DOCSIS = 1 << 6,
- LLDP_SYSTEM_CAPABILITIES_STATION = 1 << 7,
- LLDP_SYSTEM_CAPABILITIES_CVLAN = 1 << 8,
- LLDP_SYSTEM_CAPABILITIES_SVLAN = 1 << 9,
- LLDP_SYSTEM_CAPABILITIES_TPMR = 1 << 10,
- _LLDP_SYSTEM_CAPABILITIES_MAX,
- _LLDP_SYSTEM_CAPABILITIES_INVALID = -1,
-} LLDPSystemCapabilities;
-
-typedef enum LLDPMedSubtype {
- LLDP_MED_SUBTYPE_RESERVED = 0,
- LLDP_MED_SUBTYPE_CAPABILITIES = 1,
- LLDP_MED_SUBTYPE_NETWORK_POLICY = 2,
- LLDP_MED_SUBTYPE_LOCATION_ID = 3,
- LLDP_MED_SUBTYPE_EXTENDED_PVMDI = 4,
- LLDP_MED_SUBTYPE_INV_HWREV = 5,
- LLDP_MED_SUBTYPE_INV_FWREV = 6,
- LLDP_MED_SUBTYPE_INV_SWREV = 7,
- LLDP_MED_SUBTYPE_INV_SERIAL = 8,
- LLDP_MED_SUBTYPE_INV_MANUFACTURER = 9,
- LLDP_MED_SUBTYPE_INV_MODELNAME = 10,
- LLDP_MED_SUBTYPE_INV_ASSETID = 11,
- _LLDP_MED_SUBTYPE_MAX,
- _LLDP_MED_SUBTYPE_INVALID = -1,
-} LLDPMedSubtype;
-
-typedef enum LLDPMedCapability {
- LLDP_MED_CAPABILITY_CAPAPILITIES = 1 << 0,
- LLDP_MED_CAPABILITY_NETWORK_POLICY = 1 << 1,
- LLDP_MED_CAPABILITY_LOCATION_ID = 1 << 2,
- LLDP_MED_CAPABILITY_EXTENDED_PSE = 1 << 3,
- LLDP_MED_CAPABILITY_EXTENDED_PD = 1 << 4,
- LLDP_MED_CAPABILITY_INVENTORY = 1 << 5,
- LLDP_MED_CAPABILITY_MAX,
- LLDP_MED_CAPABILITY_INVALID = -1,
-} LLDPMedCapability;
diff --git a/src/libsystemd-network/ndisc-internal.h b/src/libsystemd-network/ndisc-internal.h
new file mode 100644
index 0000000000..60e183ff8c
--- /dev/null
+++ b/src/libsystemd-network/ndisc-internal.h
@@ -0,0 +1,49 @@
+#pragma once
+
+/***
+ This file is part of systemd.
+
+ Copyright (C) 2014 Intel Corporation. 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 "log.h"
+
+#include "sd-ndisc.h"
+
+struct sd_ndisc {
+ unsigned n_ref;
+
+ int ifindex;
+ int fd;
+
+ sd_event *event;
+ int event_priority;
+
+ struct ether_addr mac_addr;
+ uint8_t hop_limit;
+ uint32_t mtu;
+
+ sd_event_source *recv_event_source;
+ sd_event_source *timeout_event_source;
+
+ unsigned nd_sent;
+
+ sd_ndisc_callback_t callback;
+ void *userdata;
+};
+
+#define log_ndisc_errno(error, fmt, ...) log_internal(LOG_DEBUG, error, __FILE__, __LINE__, __func__, "NDISC: " fmt, ##__VA_ARGS__)
+#define log_ndisc(fmt, ...) log_ndisc_errno(0, fmt, ##__VA_ARGS__)
diff --git a/src/libsystemd-network/ndisc-router.c b/src/libsystemd-network/ndisc-router.c
new file mode 100644
index 0000000000..d9950b638c
--- /dev/null
+++ b/src/libsystemd-network/ndisc-router.c
@@ -0,0 +1,779 @@
+/***
+ This file is part of systemd.
+
+ Copyright (C) 2014 Intel Corporation. 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/icmp6.h>
+
+#include "sd-ndisc.h"
+
+#include "alloc-util.h"
+#include "dns-domain.h"
+#include "hostname-util.h"
+#include "missing.h"
+#include "ndisc-internal.h"
+#include "ndisc-router.h"
+#include "strv.h"
+
+_public_ sd_ndisc_router* sd_ndisc_router_ref(sd_ndisc_router *rt) {
+ if (!rt)
+ return NULL;
+
+ assert(rt->n_ref > 0);
+ rt->n_ref++;
+
+ return rt;
+}
+
+_public_ sd_ndisc_router* sd_ndisc_router_unref(sd_ndisc_router *rt) {
+ if (!rt)
+ return NULL;
+
+ assert(rt->n_ref > 0);
+ rt->n_ref--;
+
+ if (rt->n_ref > 0)
+ return NULL;
+
+ free(rt);
+ return NULL;
+}
+
+sd_ndisc_router *ndisc_router_new(size_t raw_size) {
+ sd_ndisc_router *rt;
+
+ rt = malloc0(ALIGN(sizeof(sd_ndisc_router)) + raw_size);
+ if (!rt)
+ return NULL;
+
+ rt->raw_size = raw_size;
+ rt->n_ref = 1;
+
+ return rt;
+}
+
+_public_ int sd_ndisc_router_from_raw(sd_ndisc_router **ret, const void *raw, size_t raw_size) {
+ _cleanup_(sd_ndisc_router_unrefp) sd_ndisc_router *rt = NULL;
+ int r;
+
+ assert_return(ret, -EINVAL);
+ assert_return(raw || raw_size <= 0, -EINVAL);
+
+ rt = ndisc_router_new(raw_size);
+ if (!rt)
+ return -ENOMEM;
+
+ memcpy(NDISC_ROUTER_RAW(rt), raw, raw_size);
+ r = ndisc_router_parse(rt);
+ if (r < 0)
+ return r;
+
+ *ret = rt;
+ rt = NULL;
+
+ return r;
+}
+
+_public_ int sd_ndisc_router_get_address(sd_ndisc_router *rt, struct in6_addr *ret_addr) {
+ assert_return(rt, -EINVAL);
+ assert_return(ret_addr, -EINVAL);
+
+ if (in6_addr_is_null(&rt->address))
+ return -ENODATA;
+
+ *ret_addr = rt->address;
+ return 0;
+}
+
+_public_ int sd_ndisc_router_get_timestamp(sd_ndisc_router *rt, clockid_t clock, uint64_t *ret) {
+ assert_return(rt, -EINVAL);
+ assert_return(TRIPLE_TIMESTAMP_HAS_CLOCK(clock), -EOPNOTSUPP);
+ assert_return(clock_supported(clock), -EOPNOTSUPP);
+ assert_return(ret, -EINVAL);
+
+ if (!triple_timestamp_is_set(&rt->timestamp))
+ return -ENODATA;
+
+ *ret = triple_timestamp_by_clock(&rt->timestamp, clock);
+ return 0;
+}
+
+_public_ int sd_ndisc_router_get_raw(sd_ndisc_router *rt, const void **ret, size_t *size) {
+ assert_return(rt, -EINVAL);
+ assert_return(ret, -EINVAL);
+ assert_return(size, -EINVAL);
+
+ *ret = NDISC_ROUTER_RAW(rt);
+ *size = rt->raw_size;
+
+ return 0;
+}
+
+int ndisc_router_parse(sd_ndisc_router *rt) {
+ struct nd_router_advert *a;
+ const uint8_t *p;
+ bool has_mtu = false, has_flag_extension = false;
+ size_t left;
+
+ assert(rt);
+
+ if (rt->raw_size < sizeof(struct nd_router_advert)) {
+ log_ndisc("Too small to be a router advertisement, ignoring.");
+ return -EBADMSG;
+ }
+
+ /* Router advertisement packets are neatly aligned to 64bit boundaries, hence we can access them directly */
+ a = NDISC_ROUTER_RAW(rt);
+
+ if (a->nd_ra_type != ND_ROUTER_ADVERT) {
+ log_ndisc("Received ND packet that is not a router advertisement, ignoring.");
+ return -EBADMSG;
+ }
+
+ if (a->nd_ra_code != 0) {
+ log_ndisc("Received ND packet with wrong RA code, ignoring.");
+ return -EBADMSG;
+ }
+
+ rt->hop_limit = a->nd_ra_curhoplimit;
+ rt->flags = a->nd_ra_flags_reserved; /* the first 8bit */
+ rt->lifetime = be16toh(a->nd_ra_router_lifetime);
+
+ rt->preference = (rt->flags >> 3) & 3;
+ if (!IN_SET(rt->preference, SD_NDISC_PREFERENCE_LOW, SD_NDISC_PREFERENCE_HIGH))
+ rt->preference = SD_NDISC_PREFERENCE_MEDIUM;
+
+ p = (const uint8_t*) NDISC_ROUTER_RAW(rt) + sizeof(struct nd_router_advert);
+ left = rt->raw_size - sizeof(struct nd_router_advert);
+
+ for (;;) {
+ uint8_t type;
+ size_t length;
+
+ if (left == 0)
+ break;
+
+ if (left < 2) {
+ log_ndisc("Option lacks header, ignoring datagram.");
+ return -EBADMSG;
+ }
+
+ type = p[0];
+ length = p[1] * 8;
+
+ if (length == 0) {
+ log_ndisc("Zero-length option, ignoring datagram.");
+ return -EBADMSG;
+ }
+ if (left < length) {
+ log_ndisc("Option truncated, ignoring datagram.");
+ return -EBADMSG;
+ }
+
+ switch (type) {
+
+ case SD_NDISC_OPTION_PREFIX_INFORMATION:
+
+ if (length != 4*8) {
+ log_ndisc("Prefix option of invalid size, ignoring datagram.");
+ return -EBADMSG;
+ }
+
+ if (p[2] > 128) {
+ log_ndisc("Bad prefix length, ignoring datagram.");
+ return -EBADMSG;
+ }
+
+ break;
+
+ case SD_NDISC_OPTION_MTU: {
+ uint32_t m;
+
+ if (has_mtu) {
+ log_ndisc("MTU option specified twice, ignoring.");
+ continue;
+ }
+
+ if (length != 8) {
+ log_ndisc("MTU option of invalid size, ignoring datagram.");
+ return -EBADMSG;
+ }
+
+ m = be32toh(*(uint32_t*) (p + 4));
+ if (m >= IPV6_MIN_MTU) /* ignore invalidly small MTUs */
+ rt->mtu = m;
+
+ has_mtu = true;
+ break;
+ }
+
+ case SD_NDISC_OPTION_ROUTE_INFORMATION:
+ if (length < 1*8 || length > 3*8) {
+ log_ndisc("Route information option of invalid size, ignoring datagram.");
+ return -EBADMSG;
+ }
+
+ if (p[2] > 128) {
+ log_ndisc("Bad route prefix length, ignoring datagram.");
+ return -EBADMSG;
+ }
+
+ break;
+
+ case SD_NDISC_OPTION_RDNSS:
+ if (length < 3*8 || (length % (2*8)) != 1*8) {
+ log_ndisc("RDNSS option has invalid size.");
+ return -EBADMSG;
+ }
+
+ break;
+
+ case SD_NDISC_OPTION_FLAGS_EXTENSION:
+
+ if (has_flag_extension) {
+ log_ndisc("Flags extension option specified twice, ignoring.");
+ continue;
+ }
+
+ if (length < 1*8) {
+ log_ndisc("Flags extension option has invalid size.");
+ return -EBADMSG;
+ }
+
+ /* Add in the additional flags bits */
+ rt->flags |=
+ ((uint64_t) p[2] << 8) |
+ ((uint64_t) p[3] << 16) |
+ ((uint64_t) p[4] << 24) |
+ ((uint64_t) p[5] << 32) |
+ ((uint64_t) p[6] << 40) |
+ ((uint64_t) p[7] << 48);
+
+ has_flag_extension = true;
+ break;
+
+ case SD_NDISC_OPTION_DNSSL:
+ if (length < 2*8) {
+ log_ndisc("DNSSL option has invalid size.");
+ return -EBADMSG;
+ }
+
+ break;
+ }
+
+ p += length, left -= length;
+ }
+
+ rt->rindex = sizeof(struct nd_router_advert);
+ return 0;
+}
+
+_public_ int sd_ndisc_router_get_hop_limit(sd_ndisc_router *rt, uint8_t *ret) {
+ assert_return(rt, -EINVAL);
+ assert_return(ret, -EINVAL);
+
+ *ret = rt->hop_limit;
+ return 0;
+}
+
+_public_ int sd_ndisc_router_get_flags(sd_ndisc_router *rt, uint64_t *ret_flags) {
+ assert_return(rt, -EINVAL);
+ assert_return(ret_flags, -EINVAL);
+
+ *ret_flags = rt->flags;
+ return 0;
+}
+
+_public_ int sd_ndisc_router_get_lifetime(sd_ndisc_router *rt, uint16_t *ret_lifetime) {
+ assert_return(rt, -EINVAL);
+ assert_return(ret_lifetime, -EINVAL);
+
+ *ret_lifetime = rt->lifetime;
+ return 0;
+}
+
+_public_ int sd_ndisc_router_get_preference(sd_ndisc_router *rt, unsigned *ret) {
+ assert_return(rt, -EINVAL);
+ assert_return(ret, -EINVAL);
+
+ *ret = rt->preference;
+ return 0;
+}
+
+_public_ int sd_ndisc_router_get_mtu(sd_ndisc_router *rt, uint32_t *ret) {
+ assert_return(rt, -EINVAL);
+ assert_return(ret, -EINVAL);
+
+ if (rt->mtu <= 0)
+ return -ENODATA;
+
+ *ret = rt->mtu;
+ return 0;
+}
+
+_public_ int sd_ndisc_router_option_rewind(sd_ndisc_router *rt) {
+ assert_return(rt, -EINVAL);
+
+ assert(rt->raw_size >= sizeof(struct nd_router_advert));
+ rt->rindex = sizeof(struct nd_router_advert);
+
+ return rt->rindex < rt->raw_size;
+}
+
+_public_ int sd_ndisc_router_option_next(sd_ndisc_router *rt) {
+ size_t length;
+
+ assert_return(rt, -EINVAL);
+
+ if (rt->rindex == rt->raw_size) /* EOF */
+ return -ESPIPE;
+
+ if (rt->rindex + 2 > rt->raw_size) /* Truncated message */
+ return -EBADMSG;
+
+ length = NDISC_ROUTER_OPTION_LENGTH(rt);
+ if (rt->rindex + length > rt->raw_size)
+ return -EBADMSG;
+
+ rt->rindex += length;
+ return rt->rindex < rt->raw_size;
+}
+
+_public_ int sd_ndisc_router_option_get_type(sd_ndisc_router *rt, uint8_t *ret) {
+ assert_return(rt, -EINVAL);
+ assert_return(ret, -EINVAL);
+
+ if (rt->rindex == rt->raw_size) /* EOF */
+ return -ESPIPE;
+
+ if (rt->rindex + 2 > rt->raw_size) /* Truncated message */
+ return -EBADMSG;
+
+ *ret = NDISC_ROUTER_OPTION_TYPE(rt);
+ return 0;
+}
+
+_public_ int sd_ndisc_router_option_is_type(sd_ndisc_router *rt, uint8_t type) {
+ uint8_t k;
+ int r;
+
+ assert_return(rt, -EINVAL);
+
+ r = sd_ndisc_router_option_get_type(rt, &k);
+ if (r < 0)
+ return r;
+
+ return type == k;
+}
+
+_public_ int sd_ndisc_router_option_get_raw(sd_ndisc_router *rt, const void **ret, size_t *size) {
+ size_t length;
+
+ assert_return(rt, -EINVAL);
+ assert_return(ret, -EINVAL);
+ assert_return(size, -EINVAL);
+
+ /* Note that this returns the full option, including the option header */
+
+ if (rt->rindex + 2 > rt->raw_size)
+ return -EBADMSG;
+
+ length = NDISC_ROUTER_OPTION_LENGTH(rt);
+ if (rt->rindex + length > rt->raw_size)
+ return -EBADMSG;
+
+ *ret = (uint8_t*) NDISC_ROUTER_RAW(rt) + rt->rindex;
+ *size = length;
+
+ return 0;
+}
+
+static int get_prefix_info(sd_ndisc_router *rt, struct nd_opt_prefix_info **ret) {
+ struct nd_opt_prefix_info *ri;
+ size_t length;
+ int r;
+
+ assert(rt);
+ assert(ret);
+
+ r = sd_ndisc_router_option_is_type(rt, SD_NDISC_OPTION_PREFIX_INFORMATION);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return -EMEDIUMTYPE;
+
+ length = NDISC_ROUTER_OPTION_LENGTH(rt);
+ if (length != sizeof(struct nd_opt_prefix_info))
+ return -EBADMSG;
+
+ ri = (struct nd_opt_prefix_info*) ((uint8_t*) NDISC_ROUTER_RAW(rt) + rt->rindex);
+ if (ri->nd_opt_pi_prefix_len > 128)
+ return -EBADMSG;
+
+ *ret = ri;
+ return 0;
+}
+
+_public_ int sd_ndisc_router_prefix_get_valid_lifetime(sd_ndisc_router *rt, uint32_t *ret) {
+ struct nd_opt_prefix_info *ri;
+ int r;
+
+ assert_return(rt, -EINVAL);
+ assert_return(ret, -EINVAL);
+
+ r = get_prefix_info(rt, &ri);
+ if (r < 0)
+ return r;
+
+ *ret = be32toh(ri->nd_opt_pi_valid_time);
+ return 0;
+}
+
+_public_ int sd_ndisc_router_prefix_get_preferred_lifetime(sd_ndisc_router *rt, uint32_t *ret) {
+ struct nd_opt_prefix_info *pi;
+ int r;
+
+ assert_return(rt, -EINVAL);
+ assert_return(ret, -EINVAL);
+
+ r = get_prefix_info(rt, &pi);
+ if (r < 0)
+ return r;
+
+ *ret = be32toh(pi->nd_opt_pi_preferred_time);
+ return 0;
+}
+
+_public_ int sd_ndisc_router_prefix_get_flags(sd_ndisc_router *rt, uint8_t *ret) {
+ struct nd_opt_prefix_info *pi;
+ int r;
+
+ assert_return(rt, -EINVAL);
+ assert_return(ret, -EINVAL);
+
+ r = get_prefix_info(rt, &pi);
+ if (r < 0)
+ return r;
+
+ *ret = pi->nd_opt_pi_flags_reserved;
+ return 0;
+}
+
+_public_ int sd_ndisc_router_prefix_get_address(sd_ndisc_router *rt, struct in6_addr *ret_addr) {
+ struct nd_opt_prefix_info *pi;
+ int r;
+
+ assert_return(rt, -EINVAL);
+ assert_return(ret_addr, -EINVAL);
+
+ r = get_prefix_info(rt, &pi);
+ if (r < 0)
+ return r;
+
+ *ret_addr = pi->nd_opt_pi_prefix;
+ return 0;
+}
+
+_public_ int sd_ndisc_router_prefix_get_prefixlen(sd_ndisc_router *rt, unsigned *ret) {
+ struct nd_opt_prefix_info *pi;
+ int r;
+
+ assert_return(rt, -EINVAL);
+ assert_return(ret, -EINVAL);
+
+ r = get_prefix_info(rt, &pi);
+ if (r < 0)
+ return r;
+
+ if (pi->nd_opt_pi_prefix_len > 128)
+ return -EBADMSG;
+
+ *ret = pi->nd_opt_pi_prefix_len;
+ return 0;
+}
+
+static int get_route_info(sd_ndisc_router *rt, uint8_t **ret) {
+ uint8_t *ri;
+ size_t length;
+ int r;
+
+ assert(rt);
+ assert(ret);
+
+ r = sd_ndisc_router_option_is_type(rt, SD_NDISC_OPTION_ROUTE_INFORMATION);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return -EMEDIUMTYPE;
+
+ length = NDISC_ROUTER_OPTION_LENGTH(rt);
+ if (length < 1*8 || length > 3*8)
+ return -EBADMSG;
+
+ ri = (uint8_t*) NDISC_ROUTER_RAW(rt) + rt->rindex;
+
+ if (ri[2] > 128)
+ return -EBADMSG;
+
+ *ret = ri;
+ return 0;
+}
+
+_public_ int sd_ndisc_router_route_get_lifetime(sd_ndisc_router *rt, uint32_t *ret) {
+ uint8_t *ri;
+ int r;
+
+ assert_return(rt, -EINVAL);
+ assert_return(ret, -EINVAL);
+
+ r = get_route_info(rt, &ri);
+ if (r < 0)
+ return r;
+
+ *ret = be32toh(*(uint32_t*) (ri + 4));
+ return 0;
+}
+
+_public_ int sd_ndisc_router_route_get_address(sd_ndisc_router *rt, struct in6_addr *ret_addr) {
+ uint8_t *ri;
+ int r;
+
+ assert_return(rt, -EINVAL);
+ assert_return(ret_addr, -EINVAL);
+
+ r = get_route_info(rt, &ri);
+ if (r < 0)
+ return r;
+
+ zero(*ret_addr);
+ memcpy(ret_addr, ri + 8, NDISC_ROUTER_OPTION_LENGTH(rt) - 8);
+
+ return 0;
+}
+
+_public_ int sd_ndisc_router_route_get_prefixlen(sd_ndisc_router *rt, unsigned *ret) {
+ uint8_t *ri;
+ int r;
+
+ assert_return(rt, -EINVAL);
+ assert_return(ret, -EINVAL);
+
+ r = get_route_info(rt, &ri);
+ if (r < 0)
+ return r;
+
+ *ret = ri[2];
+ return 0;
+}
+
+_public_ int sd_ndisc_router_route_get_preference(sd_ndisc_router *rt, unsigned *ret) {
+ uint8_t *ri;
+ int r;
+
+ assert_return(rt, -EINVAL);
+ assert_return(ret, -EINVAL);
+
+ r = get_route_info(rt, &ri);
+ if (r < 0)
+ return r;
+
+ *ret = (ri[3] >> 3) & 3;
+ if (!IN_SET(*ret, SD_NDISC_PREFERENCE_LOW, SD_NDISC_PREFERENCE_HIGH))
+ *ret = SD_NDISC_PREFERENCE_MEDIUM;
+
+ return 0;
+}
+
+static int get_rdnss_info(sd_ndisc_router *rt, uint8_t **ret) {
+ size_t length;
+ int r;
+
+ assert(rt);
+ assert(ret);
+
+ r = sd_ndisc_router_option_is_type(rt, SD_NDISC_OPTION_RDNSS);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return -EMEDIUMTYPE;
+
+ length = NDISC_ROUTER_OPTION_LENGTH(rt);
+ if (length < 3*8 || (length % (2*8)) != 1*8)
+ return -EBADMSG;
+
+ *ret = (uint8_t*) NDISC_ROUTER_RAW(rt) + rt->rindex;
+ return 0;
+}
+
+_public_ int sd_ndisc_router_rdnss_get_addresses(sd_ndisc_router *rt, const struct in6_addr **ret) {
+ uint8_t *ri;
+ int r;
+
+ assert_return(rt, -EINVAL);
+ assert_return(ret, -EINVAL);
+
+ r = get_rdnss_info(rt, &ri);
+ if (r < 0)
+ return r;
+
+ *ret = (const struct in6_addr*) (ri + 8);
+ return (NDISC_ROUTER_OPTION_LENGTH(rt) - 8) / 16;
+}
+
+_public_ int sd_ndisc_router_rdnss_get_lifetime(sd_ndisc_router *rt, uint32_t *ret) {
+ uint8_t *ri;
+ int r;
+
+ assert_return(rt, -EINVAL);
+ assert_return(ret, -EINVAL);
+
+ r = get_rdnss_info(rt, &ri);
+ if (r < 0)
+ return r;
+
+ *ret = be32toh(*(uint32_t*) (ri + 4));
+ return 0;
+}
+
+static int get_dnssl_info(sd_ndisc_router *rt, uint8_t **ret) {
+ size_t length;
+ int r;
+
+ assert(rt);
+ assert(ret);
+
+ r = sd_ndisc_router_option_is_type(rt, SD_NDISC_OPTION_DNSSL);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return -EMEDIUMTYPE;
+
+ length = NDISC_ROUTER_OPTION_LENGTH(rt);
+ if (length < 2*8)
+ return -EBADMSG;
+
+ *ret = (uint8_t*) NDISC_ROUTER_RAW(rt) + rt->rindex;
+ return 0;
+}
+
+_public_ int sd_ndisc_router_dnssl_get_domains(sd_ndisc_router *rt, char ***ret) {
+ _cleanup_strv_free_ char **l = NULL;
+ _cleanup_free_ char *e = NULL;
+ size_t allocated = 0, n = 0, left;
+ uint8_t *ri, *p;
+ bool first = true;
+ int r;
+ unsigned k = 0;
+
+ assert_return(rt, -EINVAL);
+ assert_return(ret, -EINVAL);
+
+ r = get_dnssl_info(rt, &ri);
+ if (r < 0)
+ return r;
+
+ p = ri + 8;
+ left = NDISC_ROUTER_OPTION_LENGTH(rt) - 8;
+
+ for (;;) {
+ if (left == 0) {
+
+ if (n > 0) /* Not properly NUL terminated */
+ return -EBADMSG;
+
+ break;
+ }
+
+ if (*p == 0) {
+ /* Found NUL termination */
+
+ if (n > 0) {
+ _cleanup_free_ char *normalized = NULL;
+
+ e[n] = 0;
+ r = dns_name_normalize(e, &normalized);
+ if (r < 0)
+ return r;
+
+ /* Ignore the root domain name or "localhost" and friends */
+ if (!is_localhost(normalized) &&
+ !dns_name_is_root(normalized)) {
+
+ if (strv_push(&l, normalized) < 0)
+ return -ENOMEM;
+
+ normalized = NULL;
+ k++;
+ }
+ }
+
+ n = 0;
+ first = true;
+ p++, left--;
+ continue;
+ }
+
+ /* Check for compression (which is not allowed) */
+ if (*p > 63)
+ return -EBADMSG;
+
+ if (1U + *p + 1U > left)
+ return -EBADMSG;
+
+ if (!GREEDY_REALLOC(e, allocated, n + !first + DNS_LABEL_ESCAPED_MAX + 1U))
+ return -ENOMEM;
+
+ if (first)
+ first = false;
+ else
+ e[n++] = '.';
+
+ r = dns_label_escape((char*) p+1, *p, e + n, DNS_LABEL_ESCAPED_MAX);
+ if (r < 0)
+ return r;
+
+ n += r;
+
+ left -= 1 + *p;
+ p += 1 + *p;
+ }
+
+ if (strv_isempty(l)) {
+ *ret = NULL;
+ return 0;
+ }
+
+ *ret = l;
+ l = NULL;
+
+ return k;
+}
+
+_public_ int sd_ndisc_router_dnssl_get_lifetime(sd_ndisc_router *rt, uint32_t *ret_sec) {
+ uint8_t *ri;
+ int r;
+
+ assert_return(rt, -EINVAL);
+ assert_return(ret_sec, -EINVAL);
+
+ r = get_dnssl_info(rt, &ri);
+ if (r < 0)
+ return r;
+
+ *ret_sec = be32toh(*(uint32_t*) (ri + 4));
+ return 0;
+}
diff --git a/src/libsystemd-network/ndisc-router.h b/src/libsystemd-network/ndisc-router.h
new file mode 100644
index 0000000000..1fe703da63
--- /dev/null
+++ b/src/libsystemd-network/ndisc-router.h
@@ -0,0 +1,62 @@
+#pragma once
+
+/***
+ This file is part of systemd.
+
+ Copyright (C) 2014 Intel Corporation. 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 "sd-ndisc.h"
+
+#include "time-util.h"
+
+struct sd_ndisc_router {
+ unsigned n_ref;
+
+ triple_timestamp timestamp;
+ struct in6_addr address;
+
+ /* The raw packet size. The data is appended to the object, accessible via NDIS_ROUTER_RAW() */
+ size_t raw_size;
+
+ /* The current read index for the iterative option interface */
+ size_t rindex;
+
+ uint64_t flags;
+ unsigned preference;
+ uint16_t lifetime;
+
+ uint8_t hop_limit;
+ uint32_t mtu;
+};
+
+static inline void* NDISC_ROUTER_RAW(const sd_ndisc_router *rt) {
+ return (uint8_t*) rt + ALIGN(sizeof(sd_ndisc_router));
+}
+
+static inline void *NDISC_ROUTER_OPTION_DATA(const sd_ndisc_router *rt) {
+ return ((uint8_t*) NDISC_ROUTER_RAW(rt)) + rt->rindex;
+}
+
+static inline uint8_t NDISC_ROUTER_OPTION_TYPE(const sd_ndisc_router *rt) {
+ return ((uint8_t*) NDISC_ROUTER_OPTION_DATA(rt))[0];
+}
+static inline size_t NDISC_ROUTER_OPTION_LENGTH(const sd_ndisc_router *rt) {
+ return ((uint8_t*) NDISC_ROUTER_OPTION_DATA(rt))[1] * 8;
+}
+
+sd_ndisc_router *ndisc_router_new(size_t raw_size);
+int ndisc_router_parse(sd_ndisc_router *rt);
diff --git a/src/libsystemd-network/network-internal.c b/src/libsystemd-network/network-internal.c
index d579755cc8..9d78b953fc 100644
--- a/src/libsystemd-network/network-internal.c
+++ b/src/libsystemd-network/network-internal.c
@@ -1,5 +1,3 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
/***
This file is part of systemd.
@@ -19,19 +17,27 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <netinet/ether.h>
-#include <linux/if.h>
#include <arpa/inet.h>
+#include <linux/if.h>
+#include <netinet/ether.h>
-#include "strv.h"
-#include "siphash24.h"
+#include "sd-ndisc.h"
+
+#include "alloc-util.h"
+#include "condition.h"
+#include "conf-parser.h"
#include "dhcp-lease-internal.h"
+#include "ether-addr-util.h"
+#include "hexdecoct.h"
#include "log.h"
+#include "network-internal.h"
+#include "parse-util.h"
+#include "siphash24.h"
+#include "socket-util.h"
+#include "string-util.h"
+#include "strv.h"
#include "utf8.h"
#include "util.h"
-#include "conf-parser.h"
-#include "condition.h"
-#include "network-internal.h"
const char *net_get_name(struct udev_device *device) {
const char *name, *field;
@@ -50,7 +56,7 @@ const char *net_get_name(struct udev_device *device) {
#define HASH_KEY SD_ID128_MAKE(d3,1e,48,fa,90,fe,4b,4c,9d,af,d5,d7,a1,b1,2e,8a)
-int net_get_unique_predictable_data(struct udev_device *device, uint8_t result[8]) {
+int net_get_unique_predictable_data(struct udev_device *device, uint64_t *result) {
size_t l, sz = 0;
const char *name = NULL;
int r;
@@ -75,7 +81,7 @@ int net_get_unique_predictable_data(struct udev_device *device, uint8_t result[8
/* Let's hash the machine ID plus the device name. We
* use a fixed, but originally randomly created hash
* key here. */
- siphash24(result, v, sz, HASH_KEY.bytes);
+ *result = htole64(siphash24(v, sz, HASH_KEY.bytes));
return 0;
}
@@ -96,16 +102,16 @@ bool net_match_config(const struct ether_addr *match_mac,
const char *dev_type,
const char *dev_name) {
- if (match_host && !condition_test(match_host))
+ if (match_host && condition_test(match_host) <= 0)
return false;
- if (match_virt && !condition_test(match_virt))
+ if (match_virt && condition_test(match_virt) <= 0)
return false;
- if (match_kernel && !condition_test(match_kernel))
+ if (match_kernel && condition_test(match_kernel) <= 0)
return false;
- if (match_arch && !condition_test(match_arch))
+ if (match_arch && condition_test(match_arch) <= 0)
return false;
if (match_mac && (!dev_mac || memcmp(match_mac, dev_mac, ETH_ALEN)))
@@ -171,59 +177,19 @@ int config_parse_net_condition(const char *unit,
return 0;
}
-int config_parse_ifname(const char *unit,
- const char *filename,
- unsigned line,
- const char *section,
- unsigned section_line,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata) {
-
- char **s = data;
- _cleanup_free_ char *n = NULL;
-
- assert(filename);
- assert(lvalue);
- assert(rvalue);
- assert(data);
-
- n = strdup(rvalue);
- if (!n)
- return log_oom();
-
- if (!ascii_is_valid(n) || strlen(n) >= IFNAMSIZ) {
- log_syntax(unit, LOG_ERR, filename, line, EINVAL,
- "Interface name is not ASCII clean or is too long, ignoring assignment: %s", rvalue);
- return 0;
- }
-
- free(*s);
- if (*n) {
- *s = n;
- n = NULL;
- } else
- *s = NULL;
-
- return 0;
-}
-
-int config_parse_ifnames(const char *unit,
- const char *filename,
- unsigned line,
- const char *section,
- unsigned section_line,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata) {
+int config_parse_ifnames(
+ const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ unsigned section_line,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
char ***sv = data;
- const char *word, *state;
- size_t l;
int r;
assert(filename);
@@ -231,23 +197,27 @@ int config_parse_ifnames(const char *unit,
assert(rvalue);
assert(data);
- FOREACH_WORD(word, l, rvalue, state) {
- char *n;
+ for (;;) {
+ _cleanup_free_ char *word = NULL;
- n = strndup(word, l);
- if (!n)
- return log_oom();
+ r = extract_first_word(&rvalue, &word, NULL, 0);
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse interface name list: %s", rvalue);
+ return 0;
+ }
+ if (r == 0)
+ break;
- if (!ascii_is_valid(n) || strlen(n) >= IFNAMSIZ) {
- log_syntax(unit, LOG_ERR, filename, line, EINVAL,
- "Interface name is not ASCII clean or is too long, ignoring assignment: %s", rvalue);
- free(n);
+ if (!ifname_valid(word)) {
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Interface name is not valid or too long, ignoring assignment: %s", rvalue);
return 0;
}
- r = strv_consume(sv, n);
+ r = strv_push(sv, word);
if (r < 0)
return log_oom();
+
+ word = NULL;
}
return 0;
@@ -277,8 +247,7 @@ int config_parse_ifalias(const char *unit,
return log_oom();
if (!ascii_is_valid(n) || strlen(n) >= IFALIASZ) {
- log_syntax(unit, LOG_ERR, filename, line, EINVAL,
- "Interface alias is not ASCII clean or is too long, ignoring assignment: %s", rvalue);
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Interface alias is not ASCII clean or is too long, ignoring assignment: %s", rvalue);
return 0;
}
@@ -304,6 +273,8 @@ int config_parse_hwaddr(const char *unit,
void *userdata) {
struct ether_addr **hwaddr = data;
struct ether_addr *n;
+ const char *start;
+ size_t offset;
int r;
assert(filename);
@@ -315,16 +286,11 @@ int config_parse_hwaddr(const char *unit,
if (!n)
return log_oom();
- r = sscanf(rvalue, "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx",
- &n->ether_addr_octet[0],
- &n->ether_addr_octet[1],
- &n->ether_addr_octet[2],
- &n->ether_addr_octet[3],
- &n->ether_addr_octet[4],
- &n->ether_addr_octet[5]);
- if (r != 6) {
- log_syntax(unit, LOG_ERR, filename, line, EINVAL,
- "Not a valid MAC address, ignoring assignment: %s", rvalue);
+ start = rvalue + strspn(rvalue, WHITESPACE);
+ r = ether_addr_from_string(start, n, &offset);
+
+ if (r || (start[offset + strspn(start + offset, WHITESPACE)] != '\0')) {
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Not a valid MAC address, ignoring assignment: %s", rvalue);
free(n);
return 0;
}
@@ -335,6 +301,36 @@ int config_parse_hwaddr(const char *unit,
return 0;
}
+int config_parse_iaid(const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ unsigned section_line,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+ uint32_t iaid;
+ int r;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+ assert(data);
+
+ r = safe_atou32(rvalue, &iaid);
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, r,
+ "Unable to read IAID, ignoring assignment: %s", rvalue);
+ return 0;
+ }
+
+ *((uint32_t *)data) = iaid;
+
+ return 0;
+}
+
void serialize_in_addrs(FILE *f, const struct in_addr *addresses, size_t size) {
unsigned i;
@@ -350,32 +346,32 @@ void serialize_in_addrs(FILE *f, const struct in_addr *addresses, size_t size) {
int deserialize_in_addrs(struct in_addr **ret, const char *string) {
_cleanup_free_ struct in_addr *addresses = NULL;
int size = 0;
- const char *word, *state;
- size_t len;
assert(ret);
assert(string);
- FOREACH_WORD(word, len, string, state) {
- _cleanup_free_ char *addr_str = NULL;
+ for (;;) {
+ _cleanup_free_ char *word = NULL;
struct in_addr *new_addresses;
int r;
+ r = extract_first_word(&string, &word, NULL, 0);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ break;
+
new_addresses = realloc(addresses, (size + 1) * sizeof(struct in_addr));
if (!new_addresses)
return -ENOMEM;
else
addresses = new_addresses;
- addr_str = strndup(word, len);
- if (!addr_str)
- return -ENOMEM;
-
- r = inet_pton(AF_INET, addr_str, &(addresses[size]));
+ r = inet_pton(AF_INET, word, &(addresses[size]));
if (r <= 0)
continue;
- size ++;
+ size++;
}
*ret = addresses;
@@ -384,31 +380,48 @@ int deserialize_in_addrs(struct in_addr **ret, const char *string) {
return size;
}
+void serialize_in6_addrs(FILE *f, const struct in6_addr *addresses, size_t size) {
+ unsigned i;
+
+ assert(f);
+ assert(addresses);
+ assert(size);
+
+ for (i = 0; i < size; i++) {
+ char buffer[INET6_ADDRSTRLEN];
+
+ fputs(inet_ntop(AF_INET6, addresses+i, buffer, sizeof(buffer)), f);
+
+ if (i < size - 1)
+ fputc(' ', f);
+ }
+}
+
int deserialize_in6_addrs(struct in6_addr **ret, const char *string) {
_cleanup_free_ struct in6_addr *addresses = NULL;
int size = 0;
- const char *word, *state;
- size_t len;
assert(ret);
assert(string);
- FOREACH_WORD(word, len, string, state) {
- _cleanup_free_ char *addr_str = NULL;
+ for (;;) {
+ _cleanup_free_ char *word = NULL;
struct in6_addr *new_addresses;
int r;
+ r = extract_first_word(&string, &word, NULL, 0);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ break;
+
new_addresses = realloc(addresses, (size + 1) * sizeof(struct in6_addr));
if (!new_addresses)
return -ENOMEM;
else
addresses = new_addresses;
- addr_str = strndup(word, len);
- if (!addr_str)
- return -ENOMEM;
-
- r = inet_pton(AF_INET6, addr_str, &(addresses[size]));
+ r = inet_pton(AF_INET6, word, &(addresses[size]));
if (r <= 0)
continue;
@@ -421,7 +434,7 @@ int deserialize_in6_addrs(struct in6_addr **ret, const char *string) {
return size;
}
-void serialize_dhcp_routes(FILE *f, const char *key, struct sd_dhcp_route *routes, size_t size) {
+void serialize_dhcp_routes(FILE *f, const char *key, sd_dhcp_route **routes, size_t size) {
unsigned i;
assert(f);
@@ -432,10 +445,15 @@ void serialize_dhcp_routes(FILE *f, const char *key, struct sd_dhcp_route *route
fprintf(f, "%s=", key);
for (i = 0; i < size; i++) {
- fprintf(f, "%s/%" PRIu8, inet_ntoa(routes[i].dst_addr),
- routes[i].dst_prefixlen);
- fprintf(f, ",%s%s", inet_ntoa(routes[i].gw_addr),
- (i < (size - 1)) ? " ": "");
+ struct in_addr dest, gw;
+ uint8_t length;
+
+ assert_se(sd_dhcp_route_get_destination(routes[i], &dest) >= 0);
+ assert_se(sd_dhcp_route_get_gateway(routes[i], &gw) >= 0);
+ assert_se(sd_dhcp_route_get_destination_prefix_length(routes[i], &length) >= 0);
+
+ fprintf(f, "%s/%" PRIu8, inet_ntoa(dest), length);
+ fprintf(f, ",%s%s", inet_ntoa(gw), (i < (size - 1)) ? " ": "");
}
fputs("\n", f);
@@ -444,29 +462,29 @@ void serialize_dhcp_routes(FILE *f, const char *key, struct sd_dhcp_route *route
int deserialize_dhcp_routes(struct sd_dhcp_route **ret, size_t *ret_size, size_t *ret_allocated, const char *string) {
_cleanup_free_ struct sd_dhcp_route *routes = NULL;
size_t size = 0, allocated = 0;
- const char *word, *state;
- size_t len;
assert(ret);
assert(ret_size);
assert(ret_allocated);
assert(string);
- FOREACH_WORD(word, len, string, state) {
- /* WORD FORMAT: dst_ip/dst_prefixlen,gw_ip */
- _cleanup_free_ char* entry = NULL;
+ /* WORD FORMAT: dst_ip/dst_prefixlen,gw_ip */
+ for (;;) {
+ _cleanup_free_ char *word = NULL;
char *tok, *tok_end;
unsigned n;
int r;
- if (!GREEDY_REALLOC(routes, allocated, size + 1))
- return -ENOMEM;
+ r = extract_first_word(&string, &word, NULL, 0);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ break;
- entry = strndup(word, len);
- if(!entry)
+ if (!GREEDY_REALLOC(routes, allocated, size + 1))
return -ENOMEM;
- tok = entry;
+ tok = word;
/* get the subnet */
tok_end = strchr(tok, '/');
@@ -509,3 +527,30 @@ int deserialize_dhcp_routes(struct sd_dhcp_route **ret, size_t *ret_size, size_t
return 0;
}
+
+int serialize_dhcp_option(FILE *f, const char *key, const void *data, size_t size) {
+ _cleanup_free_ char *hex_buf = NULL;
+
+ assert(f);
+ assert(key);
+ assert(data);
+
+ hex_buf = hexmem(data, size);
+ if (hex_buf == NULL)
+ return -ENOMEM;
+
+ fprintf(f, "%s=%s\n", key, hex_buf);
+
+ return 0;
+}
+
+int deserialize_dhcp_option(void **data, size_t *data_len, const char *string) {
+ assert(data);
+ assert(data_len);
+ assert(string);
+
+ if (strlen(string) % 2)
+ return -EINVAL;
+
+ return unhexmem(string, strlen(string), (void **)data, data_len);
+}
diff --git a/src/libsystemd-network/network-internal.h b/src/libsystemd-network/network-internal.h
index 06aba893ce..5bcd577167 100644
--- a/src/libsystemd-network/network-internal.h
+++ b/src/libsystemd-network/network-internal.h
@@ -1,5 +1,3 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
#pragma once
/***
@@ -23,8 +21,10 @@
#include <stdbool.h>
-#include "udev.h"
+#include "sd-dhcp-lease.h"
+
#include "condition.h"
+#include "udev.h"
bool net_match_config(const struct ether_addr *match_mac,
char * const *match_path,
@@ -50,10 +50,6 @@ int config_parse_hwaddr(const char *unit, const char *filename, unsigned line,
const char *section, unsigned section_line, const char *lvalue,
int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_ifname(const char *unit, const char *filename, unsigned line,
- const char *section, unsigned section_line, const char *lvalue,
- int ltype, const char *rvalue, void *data, void *userdata);
-
int config_parse_ifnames(const char *unit, const char *filename, unsigned line,
const char *section, unsigned section_line, const char *lvalue,
int ltype, const char *rvalue, void *data, void *userdata);
@@ -62,15 +58,24 @@ int config_parse_ifalias(const char *unit, const char *filename, unsigned line,
const char *section, unsigned section_line, const char *lvalue,
int ltype, const char *rvalue, void *data, void *userdata);
-int net_get_unique_predictable_data(struct udev_device *device, uint8_t result[8]);
+int config_parse_iaid(const char *unit, const char *filename, unsigned line,
+ const char *section, unsigned section_line, const char *lvalue,
+ int ltype, const char *rvalue, void *data, void *userdata);
+
+int net_get_unique_predictable_data(struct udev_device *device, uint64_t *result);
const char *net_get_name(struct udev_device *device);
void serialize_in_addrs(FILE *f, const struct in_addr *addresses, size_t size);
int deserialize_in_addrs(struct in_addr **addresses, const char *string);
+void serialize_in6_addrs(FILE *f, const struct in6_addr *addresses,
+ size_t size);
int deserialize_in6_addrs(struct in6_addr **addresses, const char *string);
/* don't include "dhcp-lease-internal.h" as it causes conflicts between netinet/ip.h and linux/ip.h */
struct sd_dhcp_route;
-void serialize_dhcp_routes(FILE *f, const char *key, struct sd_dhcp_route *routes, size_t size);
+void serialize_dhcp_routes(FILE *f, const char *key, sd_dhcp_route **routes, size_t size);
int deserialize_dhcp_routes(struct sd_dhcp_route **ret, size_t *ret_size, size_t *ret_allocated, const char *string);
+
+int serialize_dhcp_option(FILE *f, const char *key, const void *data, size_t size);
+int deserialize_dhcp_option(void **data, size_t *data_len, const char *string);
diff --git a/src/libsystemd-network/sd-dhcp-client.c b/src/libsystemd-network/sd-dhcp-client.c
index 6a0d270739..179e5950bd 100644
--- a/src/libsystemd-network/sd-dhcp-client.c
+++ b/src/libsystemd-network/sd-dhcp-client.c
@@ -17,37 +17,43 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <stdlib.h>
#include <errno.h>
-#include <string.h>
-#include <stdio.h>
#include <net/ethernet.h>
#include <net/if_arp.h>
-#include <linux/if_infiniband.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
#include <sys/ioctl.h>
+#include <linux/if_infiniband.h>
-#include "util.h"
-#include "refcnt.h"
-#include "random-util.h"
-#include "async.h"
+#include "sd-dhcp-client.h"
-#include "dhcp-protocol.h"
+#include "alloc-util.h"
+#include "async.h"
+#include "dhcp-identifier.h"
#include "dhcp-internal.h"
#include "dhcp-lease-internal.h"
-#include "dhcp-identifier.h"
-#include "sd-dhcp-client.h"
+#include "dhcp-protocol.h"
+#include "dns-domain.h"
+#include "hostname-util.h"
+#include "random-util.h"
+#include "string-util.h"
+#include "util.h"
#define MAX_CLIENT_ID_LEN (sizeof(uint32_t) + MAX_DUID_LEN) /* Arbitrary limit */
#define MAX_MAC_ADDR_LEN CONST_MAX(INFINIBAND_ALEN, ETH_ALEN)
+#define RESTART_AFTER_NAK_MIN_USEC (1 * USEC_PER_SEC)
+#define RESTART_AFTER_NAK_MAX_USEC (30 * USEC_PER_MINUTE)
+
struct sd_dhcp_client {
- RefCount n_ref;
+ unsigned n_ref;
DHCPState state;
sd_event *event;
int event_priority;
sd_event_source *timeout_resend;
- int index;
+ int ifindex;
int fd;
union sockaddr_union link;
sd_event_source *receive_message;
@@ -76,7 +82,7 @@ struct sd_dhcp_client {
} _packed_ ll;
struct {
/* 255: Node-specific (RFC 4361) */
- uint32_t iaid;
+ be32_t iaid;
struct duid duid;
} _packed_ ns;
struct {
@@ -95,31 +101,40 @@ struct sd_dhcp_client {
sd_event_source *timeout_t1;
sd_event_source *timeout_t2;
sd_event_source *timeout_expire;
- sd_dhcp_client_cb_t cb;
+ sd_dhcp_client_callback_t callback;
void *userdata;
sd_dhcp_lease *lease;
+ usec_t start_delay;
};
static const uint8_t default_req_opts[] = {
- DHCP_OPTION_SUBNET_MASK,
- DHCP_OPTION_ROUTER,
- DHCP_OPTION_HOST_NAME,
- DHCP_OPTION_DOMAIN_NAME,
- DHCP_OPTION_DOMAIN_NAME_SERVER,
- DHCP_OPTION_NTP_SERVER,
+ SD_DHCP_OPTION_SUBNET_MASK,
+ SD_DHCP_OPTION_ROUTER,
+ SD_DHCP_OPTION_HOST_NAME,
+ SD_DHCP_OPTION_DOMAIN_NAME,
+ SD_DHCP_OPTION_DOMAIN_NAME_SERVER,
};
-static int client_receive_message_raw(sd_event_source *s, int fd,
- uint32_t revents, void *userdata);
-static int client_receive_message_udp(sd_event_source *s, int fd,
- uint32_t revents, void *userdata);
+static int client_receive_message_raw(
+ sd_event_source *s,
+ int fd,
+ uint32_t revents,
+ void *userdata);
+static int client_receive_message_udp(
+ sd_event_source *s,
+ int fd,
+ uint32_t revents,
+ void *userdata);
static void client_stop(sd_dhcp_client *client, int error);
-int sd_dhcp_client_set_callback(sd_dhcp_client *client, sd_dhcp_client_cb_t cb,
- void *userdata) {
+int sd_dhcp_client_set_callback(
+ sd_dhcp_client *client,
+ sd_dhcp_client_callback_t cb,
+ void *userdata) {
+
assert_return(client, -EINVAL);
- client->cb = cb;
+ client->callback = cb;
client->userdata = userdata;
return 0;
@@ -137,15 +152,15 @@ int sd_dhcp_client_set_request_option(sd_dhcp_client *client, uint8_t option) {
size_t i;
assert_return(client, -EINVAL);
- assert_return (IN_SET(client->state, DHCP_STATE_INIT,
- DHCP_STATE_STOPPED), -EBUSY);
+ assert_return(IN_SET(client->state, DHCP_STATE_INIT, DHCP_STATE_STOPPED), -EBUSY);
switch(option) {
- case DHCP_OPTION_PAD:
- case DHCP_OPTION_OVERLOAD:
- case DHCP_OPTION_MESSAGE_TYPE:
- case DHCP_OPTION_PARAMETER_REQUEST_LIST:
- case DHCP_OPTION_END:
+
+ case SD_DHCP_OPTION_PAD:
+ case SD_DHCP_OPTION_OVERLOAD:
+ case SD_DHCP_OPTION_MESSAGE_TYPE:
+ case SD_DHCP_OPTION_PARAMETER_REQUEST_LIST:
+ case SD_DHCP_OPTION_END:
return -EINVAL;
default:
@@ -165,11 +180,12 @@ int sd_dhcp_client_set_request_option(sd_dhcp_client *client, uint8_t option) {
return 0;
}
-int sd_dhcp_client_set_request_address(sd_dhcp_client *client,
- const struct in_addr *last_addr) {
+int sd_dhcp_client_set_request_address(
+ sd_dhcp_client *client,
+ const struct in_addr *last_addr) {
+
assert_return(client, -EINVAL);
- assert_return (IN_SET(client->state, DHCP_STATE_INIT,
- DHCP_STATE_STOPPED), -EBUSY);
+ assert_return(IN_SET(client->state, DHCP_STATE_INIT, DHCP_STATE_STOPPED), -EBUSY);
if (last_addr)
client->last_addr = last_addr->s_addr;
@@ -179,19 +195,22 @@ int sd_dhcp_client_set_request_address(sd_dhcp_client *client,
return 0;
}
-int sd_dhcp_client_set_index(sd_dhcp_client *client, int interface_index) {
- assert_return(client, -EINVAL);
- assert_return (IN_SET(client->state, DHCP_STATE_INIT,
- DHCP_STATE_STOPPED), -EBUSY);
- assert_return(interface_index > 0, -EINVAL);
+int sd_dhcp_client_set_ifindex(sd_dhcp_client *client, int ifindex) {
- client->index = interface_index;
+ assert_return(client, -EINVAL);
+ assert_return(IN_SET(client->state, DHCP_STATE_INIT, DHCP_STATE_STOPPED), -EBUSY);
+ assert_return(ifindex > 0, -EINVAL);
+ client->ifindex = ifindex;
return 0;
}
-int sd_dhcp_client_set_mac(sd_dhcp_client *client, const uint8_t *addr,
- size_t addr_len, uint16_t arp_type) {
+int sd_dhcp_client_set_mac(
+ sd_dhcp_client *client,
+ const uint8_t *addr,
+ size_t addr_len,
+ uint16_t arp_type) {
+
DHCP_CLIENT_DONT_DESTROY(client);
bool need_restart = false;
@@ -212,10 +231,9 @@ int sd_dhcp_client_set_mac(sd_dhcp_client *client, const uint8_t *addr,
return 0;
if (!IN_SET(client->state, DHCP_STATE_INIT, DHCP_STATE_STOPPED)) {
- log_dhcp_client(client, "Changing MAC address on running DHCP "
- "client, restarting");
+ log_dhcp_client(client, "Changing MAC address on running DHCP client, restarting");
need_restart = true;
- client_stop(client, DHCP_EVENT_STOP);
+ client_stop(client, SD_DHCP_CLIENT_EVENT_STOP);
}
memcpy(&client->mac_addr, addr, addr_len);
@@ -228,8 +246,11 @@ int sd_dhcp_client_set_mac(sd_dhcp_client *client, const uint8_t *addr,
return 0;
}
-int sd_dhcp_client_get_client_id(sd_dhcp_client *client, uint8_t *type,
- const uint8_t **data, size_t *data_len) {
+int sd_dhcp_client_get_client_id(
+ sd_dhcp_client *client,
+ uint8_t *type,
+ const uint8_t **data,
+ size_t *data_len) {
assert_return(client, -EINVAL);
assert_return(type, -EINVAL);
@@ -248,8 +269,12 @@ int sd_dhcp_client_get_client_id(sd_dhcp_client *client, uint8_t *type,
return 0;
}
-int sd_dhcp_client_set_client_id(sd_dhcp_client *client, uint8_t type,
- const uint8_t *data, size_t data_len) {
+int sd_dhcp_client_set_client_id(
+ sd_dhcp_client *client,
+ uint8_t type,
+ const uint8_t *data,
+ size_t data_len) {
+
DHCP_CLIENT_DONT_DESTROY(client);
bool need_restart = false;
@@ -258,14 +283,17 @@ int sd_dhcp_client_set_client_id(sd_dhcp_client *client, uint8_t type,
assert_return(data_len > 0 && data_len <= MAX_CLIENT_ID_LEN, -EINVAL);
switch (type) {
+
case ARPHRD_ETHER:
if (data_len != ETH_ALEN)
return -EINVAL;
break;
+
case ARPHRD_INFINIBAND:
if (data_len != INFINIBAND_ALEN)
return -EINVAL;
break;
+
default:
break;
}
@@ -279,7 +307,7 @@ int sd_dhcp_client_set_client_id(sd_dhcp_client *client, uint8_t type,
log_dhcp_client(client, "Changing client ID on running DHCP "
"client, restarting");
need_restart = true;
- client_stop(client, DHCP_EVENT_STOP);
+ client_stop(client, SD_DHCP_CLIENT_EVENT_STOP);
}
client->client_id.type = type;
@@ -292,12 +320,78 @@ int sd_dhcp_client_set_client_id(sd_dhcp_client *client, uint8_t type,
return 0;
}
-int sd_dhcp_client_set_hostname(sd_dhcp_client *client,
- const char *hostname) {
+/**
+ * Sets IAID and DUID. If duid is non-null, the DUID is set to duid_type + duid
+ * without further modification. Otherwise, if duid_type is supported, DUID
+ * is set based on that type. Otherwise, an error is returned.
+ */
+int sd_dhcp_client_set_iaid_duid(
+ sd_dhcp_client *client,
+ uint32_t iaid,
+ uint16_t duid_type,
+ const void *duid,
+ size_t duid_len) {
+
+ DHCP_CLIENT_DONT_DESTROY(client);
+ int r;
+ size_t len;
+
+ assert_return(client, -EINVAL);
+ assert_return(duid_len == 0 || duid != NULL, -EINVAL);
+
+ if (duid != NULL) {
+ r = dhcp_validate_duid_len(duid_type, duid_len);
+ if (r < 0)
+ return r;
+ }
+
+ zero(client->client_id);
+ client->client_id.type = 255;
+
+ /* If IAID is not configured, generate it. */
+ if (iaid == 0) {
+ r = dhcp_identifier_set_iaid(client->ifindex, client->mac_addr,
+ client->mac_addr_len,
+ &client->client_id.ns.iaid);
+ if (r < 0)
+ return r;
+ } else
+ client->client_id.ns.iaid = htobe32(iaid);
+
+ if (duid != NULL) {
+ client->client_id.ns.duid.type = htobe16(duid_type);
+ memcpy(&client->client_id.ns.duid.raw.data, duid, duid_len);
+ len = sizeof(client->client_id.ns.duid.type) + duid_len;
+ } else if (duid_type == DUID_TYPE_EN) {
+ r = dhcp_identifier_set_duid_en(&client->client_id.ns.duid, &len);
+ if (r < 0)
+ return r;
+ } else
+ return -EOPNOTSUPP;
+
+ client->client_id_len = sizeof(client->client_id.type) + len +
+ sizeof(client->client_id.ns.iaid);
+
+ if (!IN_SET(client->state, DHCP_STATE_INIT, DHCP_STATE_STOPPED)) {
+ log_dhcp_client(client, "Configured IAID+DUID, restarting.");
+ client_stop(client, SD_DHCP_CLIENT_EVENT_STOP);
+ sd_dhcp_client_start(client);
+ }
+
+ return 0;
+}
+
+int sd_dhcp_client_set_hostname(
+ sd_dhcp_client *client,
+ const char *hostname) {
+
char *new_hostname = NULL;
assert_return(client, -EINVAL);
+ if (!hostname_is_valid(hostname, false) && !dns_name_is_valid(hostname))
+ return -EINVAL;
+
if (streq_ptr(client->hostname, hostname))
return 0;
@@ -313,8 +407,10 @@ int sd_dhcp_client_set_hostname(sd_dhcp_client *client,
return 0;
}
-int sd_dhcp_client_set_vendor_class_identifier(sd_dhcp_client *client,
- const char *vci) {
+int sd_dhcp_client_set_vendor_class_identifier(
+ sd_dhcp_client *client,
+ const char *vci) {
+
char *new_vci = NULL;
assert_return(client, -EINVAL);
@@ -341,28 +437,29 @@ int sd_dhcp_client_set_mtu(sd_dhcp_client *client, uint32_t mtu) {
int sd_dhcp_client_get_lease(sd_dhcp_client *client, sd_dhcp_lease **ret) {
assert_return(client, -EINVAL);
- assert_return(ret, -EINVAL);
if (client->state != DHCP_STATE_BOUND &&
client->state != DHCP_STATE_RENEWING &&
client->state != DHCP_STATE_REBINDING)
return -EADDRNOTAVAIL;
- *ret = sd_dhcp_lease_ref(client->lease);
+ if (ret)
+ *ret = client->lease;
return 0;
}
static void client_notify(sd_dhcp_client *client, int event) {
- if (client->cb)
- client->cb(client, event, client->userdata);
+ assert(client);
+
+ if (client->callback)
+ client->callback(client, event, client->userdata);
}
static int client_initialize(sd_dhcp_client *client) {
assert_return(client, -EINVAL);
- client->receive_message =
- sd_event_source_unref(client->receive_message);
+ client->receive_message = sd_event_source_unref(client->receive_message);
client->fd = asynchronous_close(client->fd);
@@ -377,8 +474,7 @@ static int client_initialize(sd_dhcp_client *client) {
client->state = DHCP_STATE_INIT;
client->xid = 0;
- if (client->lease)
- client->lease = sd_dhcp_lease_unref(client->lease);
+ client->lease = sd_dhcp_lease_unref(client->lease);
return 0;
}
@@ -388,7 +484,7 @@ static void client_stop(sd_dhcp_client *client, int error) {
if (error < 0)
log_dhcp_client(client, "STOPPED: %s", strerror(-error));
- else if (error == DHCP_EVENT_STOP)
+ else if (error == SD_DHCP_CLIENT_EVENT_STOP)
log_dhcp_client(client, "STOPPED");
else
log_dhcp_client(client, "STOPPED: Unknown event");
@@ -398,9 +494,14 @@ static void client_stop(sd_dhcp_client *client, int error) {
client_initialize(client);
}
-static int client_message_init(sd_dhcp_client *client, DHCPPacket **ret,
- uint8_t type, size_t *_optlen, size_t *_optoffset) {
- _cleanup_free_ DHCPPacket *packet;
+static int client_message_init(
+ sd_dhcp_client *client,
+ DHCPPacket **ret,
+ uint8_t type,
+ size_t *_optlen,
+ size_t *_optoffset) {
+
+ _cleanup_free_ DHCPPacket *packet = NULL;
size_t optlen, optoffset, size;
be16_t max_size;
usec_t time_now;
@@ -467,7 +568,7 @@ static int client_message_init(sd_dhcp_client *client, DHCPPacket **ret,
client->client_id.type = 255;
- r = dhcp_identifier_set_iaid(client->index, client->mac_addr, client->mac_addr_len, &client->client_id.ns.iaid);
+ r = dhcp_identifier_set_iaid(client->ifindex, client->mac_addr, client->mac_addr_len, &client->client_id.ns.iaid);
if (r < 0)
return r;
@@ -482,7 +583,7 @@ static int client_message_init(sd_dhcp_client *client, DHCPPacket **ret,
Identifier option is not set */
if (client->client_id_len) {
r = dhcp_option_append(&packet->dhcp, optlen, &optoffset, 0,
- DHCP_OPTION_CLIENT_IDENTIFIER,
+ SD_DHCP_OPTION_CLIENT_IDENTIFIER,
client->client_id_len,
&client->client_id);
if (r < 0)
@@ -498,7 +599,7 @@ static int client_message_init(sd_dhcp_client *client, DHCPPacket **ret,
messages.
*/
r = dhcp_option_append(&packet->dhcp, optlen, &optoffset, 0,
- DHCP_OPTION_PARAMETER_REQUEST_LIST,
+ SD_DHCP_OPTION_PARAMETER_REQUEST_LIST,
client->req_opts_size, client->req_opts);
if (r < 0)
return r;
@@ -527,7 +628,7 @@ static int client_message_init(sd_dhcp_client *client, DHCPPacket **ret,
*/
max_size = htobe16(size);
r = dhcp_option_append(&packet->dhcp, client->mtu, &optoffset, 0,
- DHCP_OPTION_MAXIMUM_MESSAGE_SIZE,
+ SD_DHCP_OPTION_MAXIMUM_MESSAGE_SIZE,
2, &max_size);
if (r < 0)
return r;
@@ -540,8 +641,33 @@ static int client_message_init(sd_dhcp_client *client, DHCPPacket **ret,
return 0;
}
-static int dhcp_client_send_raw(sd_dhcp_client *client, DHCPPacket *packet,
- size_t len) {
+static int client_append_fqdn_option(
+ DHCPMessage *message,
+ size_t optlen,
+ size_t *optoffset,
+ const char *fqdn) {
+
+ uint8_t buffer[3 + DHCP_MAX_FQDN_LENGTH];
+ int r;
+
+ buffer[0] = DHCP_FQDN_FLAG_S | /* Request server to perform A RR DNS updates */
+ DHCP_FQDN_FLAG_E; /* Canonical wire format */
+ buffer[1] = 0; /* RCODE1 (deprecated) */
+ buffer[2] = 0; /* RCODE2 (deprecated) */
+
+ r = dns_name_to_wire_format(fqdn, buffer + 3, sizeof(buffer) - 3, false);
+ if (r > 0)
+ r = dhcp_option_append(message, optlen, optoffset, 0,
+ SD_DHCP_OPTION_FQDN, 3 + r, buffer);
+
+ return r;
+}
+
+static int dhcp_client_send_raw(
+ sd_dhcp_client *client,
+ DHCPPacket *packet,
+ size_t len) {
+
dhcp_packet_append_ip_headers(packet, INADDR_ANY, DHCP_PORT_CLIENT,
INADDR_BROADCAST, DHCP_PORT_SERVER, len);
@@ -571,26 +697,34 @@ static int client_send_discover(sd_dhcp_client *client) {
*/
if (client->last_addr != INADDR_ANY) {
r = dhcp_option_append(&discover->dhcp, optlen, &optoffset, 0,
- DHCP_OPTION_REQUESTED_IP_ADDRESS,
+ SD_DHCP_OPTION_REQUESTED_IP_ADDRESS,
4, &client->last_addr);
if (r < 0)
return r;
}
- /* it is unclear from RFC 2131 if client should send hostname in
- DHCPDISCOVER but dhclient does and so we do as well
- */
if (client->hostname) {
- r = dhcp_option_append(&discover->dhcp, optlen, &optoffset, 0,
- DHCP_OPTION_HOST_NAME,
- strlen(client->hostname), client->hostname);
+ /* According to RFC 4702 "clients that send the Client FQDN option in
+ their messages MUST NOT also send the Host Name option". Just send
+ one of the two depending on the hostname type.
+ */
+ if (dns_name_is_single_label(client->hostname)) {
+ /* it is unclear from RFC 2131 if client should send hostname in
+ DHCPDISCOVER but dhclient does and so we do as well
+ */
+ r = dhcp_option_append(&discover->dhcp, optlen, &optoffset, 0,
+ SD_DHCP_OPTION_HOST_NAME,
+ strlen(client->hostname), client->hostname);
+ } else
+ r = client_append_fqdn_option(&discover->dhcp, optlen, &optoffset,
+ client->hostname);
if (r < 0)
return r;
}
if (client->vendor_class_identifier) {
r = dhcp_option_append(&discover->dhcp, optlen, &optoffset, 0,
- DHCP_OPTION_VENDOR_CLASS_IDENTIFIER,
+ SD_DHCP_OPTION_VENDOR_CLASS_IDENTIFIER,
strlen(client->vendor_class_identifier),
client->vendor_class_identifier);
if (r < 0)
@@ -598,7 +732,7 @@ static int client_send_discover(sd_dhcp_client *client) {
}
r = dhcp_option_append(&discover->dhcp, optlen, &optoffset, 0,
- DHCP_OPTION_END, 0, NULL);
+ SD_DHCP_OPTION_END, 0, NULL);
if (r < 0)
return r;
@@ -620,8 +754,9 @@ static int client_send_request(sd_dhcp_client *client) {
size_t optoffset, optlen;
int r;
- r = client_message_init(client, &request, DHCP_REQUEST,
- &optlen, &optoffset);
+ assert(client);
+
+ r = client_message_init(client, &request, DHCP_REQUEST, &optlen, &optoffset);
if (r < 0)
return r;
@@ -637,13 +772,13 @@ static int client_send_request(sd_dhcp_client *client) {
*/
r = dhcp_option_append(&request->dhcp, optlen, &optoffset, 0,
- DHCP_OPTION_SERVER_IDENTIFIER,
+ SD_DHCP_OPTION_SERVER_IDENTIFIER,
4, &client->lease->server_address);
if (r < 0)
return r;
r = dhcp_option_append(&request->dhcp, optlen, &optoffset, 0,
- DHCP_OPTION_REQUESTED_IP_ADDRESS,
+ SD_DHCP_OPTION_REQUESTED_IP_ADDRESS,
4, &client->lease->address);
if (r < 0)
return r;
@@ -656,7 +791,7 @@ static int client_send_request(sd_dhcp_client *client) {
assigned address. ’ciaddr’ MUST be zero.
*/
r = dhcp_option_append(&request->dhcp, optlen, &optoffset, 0,
- DHCP_OPTION_REQUESTED_IP_ADDRESS,
+ SD_DHCP_OPTION_REQUESTED_IP_ADDRESS,
4, &client->last_addr);
if (r < 0)
return r;
@@ -689,15 +824,19 @@ static int client_send_request(sd_dhcp_client *client) {
}
if (client->hostname) {
- r = dhcp_option_append(&request->dhcp, optlen, &optoffset, 0,
- DHCP_OPTION_HOST_NAME,
- strlen(client->hostname), client->hostname);
+ if (dns_name_is_single_label(client->hostname))
+ r = dhcp_option_append(&request->dhcp, optlen, &optoffset, 0,
+ SD_DHCP_OPTION_HOST_NAME,
+ strlen(client->hostname), client->hostname);
+ else
+ r = client_append_fqdn_option(&request->dhcp, optlen, &optoffset,
+ client->hostname);
if (r < 0)
return r;
}
r = dhcp_option_append(&request->dhcp, optlen, &optoffset, 0,
- DHCP_OPTION_END, 0, NULL);
+ SD_DHCP_OPTION_END, 0, NULL);
if (r < 0)
return r;
@@ -714,18 +853,23 @@ static int client_send_request(sd_dhcp_client *client) {
return r;
switch (client->state) {
+
case DHCP_STATE_REQUESTING:
log_dhcp_client(client, "REQUEST (requesting)");
break;
+
case DHCP_STATE_INIT_REBOOT:
log_dhcp_client(client, "REQUEST (init-reboot)");
break;
+
case DHCP_STATE_RENEWING:
log_dhcp_client(client, "REQUEST (renewing)");
break;
+
case DHCP_STATE_REBINDING:
log_dhcp_client(client, "REQUEST (rebinding)");
break;
+
default:
log_dhcp_client(client, "REQUEST (invalid)");
break;
@@ -736,8 +880,11 @@ static int client_send_request(sd_dhcp_client *client) {
static int client_start(sd_dhcp_client *client);
-static int client_timeout_resend(sd_event_source *s, uint64_t usec,
- void *userdata) {
+static int client_timeout_resend(
+ sd_event_source *s,
+ uint64_t usec,
+ void *userdata) {
+
sd_dhcp_client *client = userdata;
DHCP_CLIENT_DONT_DESTROY(client);
usec_t next_timeout = 0;
@@ -754,6 +901,7 @@ static int client_timeout_resend(sd_event_source *s, uint64_t usec,
goto error;
switch (client->state) {
+
case DHCP_STATE_RENEWING:
time_left = (client->lease->t2 - client->lease->t1) / 2;
@@ -881,8 +1029,10 @@ error:
return 0;
}
-static int client_initialize_io_events(sd_dhcp_client *client,
- sd_event_io_handler_t io_callback) {
+static int client_initialize_io_events(
+ sd_dhcp_client *client,
+ sd_event_io_handler_t io_callback) {
+
int r;
assert(client);
@@ -911,6 +1061,7 @@ error:
}
static int client_initialize_time_events(sd_dhcp_client *client) {
+ uint64_t usec = 0;
int r;
assert(client);
@@ -918,10 +1069,15 @@ static int client_initialize_time_events(sd_dhcp_client *client) {
client->timeout_resend = sd_event_source_unref(client->timeout_resend);
+ if (client->start_delay) {
+ assert_se(sd_event_now(client->event, clock_boottime_or_monotonic(), &usec) >= 0);
+ usec += client->start_delay;
+ }
+
r = sd_event_add_time(client->event,
&client->timeout_resend,
clock_boottime_or_monotonic(),
- 0, 0,
+ usec, 0,
client_timeout_resend, client);
if (r < 0)
goto error;
@@ -943,28 +1099,26 @@ error:
}
-static int client_initialize_events(sd_dhcp_client *client,
- sd_event_io_handler_t io_callback) {
+static int client_initialize_events(sd_dhcp_client *client, sd_event_io_handler_t io_callback) {
client_initialize_io_events(client, io_callback);
client_initialize_time_events(client);
return 0;
}
-static int client_start(sd_dhcp_client *client) {
+static int client_start_delayed(sd_dhcp_client *client) {
int r;
assert_return(client, -EINVAL);
assert_return(client->event, -EINVAL);
- assert_return(client->index > 0, -EINVAL);
+ assert_return(client->ifindex > 0, -EINVAL);
assert_return(client->fd < 0, -EBUSY);
assert_return(client->xid == 0, -EINVAL);
- assert_return(client->state == DHCP_STATE_INIT ||
- client->state == DHCP_STATE_INIT_REBOOT, -EBUSY);
+ assert_return(IN_SET(client->state, DHCP_STATE_INIT, DHCP_STATE_INIT_REBOOT), -EBUSY);
client->xid = random_u32();
- r = dhcp_network_bind_raw_socket(client->index, &client->link,
+ r = dhcp_network_bind_raw_socket(client->ifindex, &client->link,
client->xid, client->mac_addr,
client->mac_addr_len, client->arp_type);
if (r < 0) {
@@ -979,14 +1133,18 @@ static int client_start(sd_dhcp_client *client) {
return client_initialize_events(client, client_receive_message_raw);
}
-static int client_timeout_expire(sd_event_source *s, uint64_t usec,
- void *userdata) {
+static int client_start(sd_dhcp_client *client) {
+ client->start_delay = 0;
+ return client_start_delayed(client);
+}
+
+static int client_timeout_expire(sd_event_source *s, uint64_t usec, void *userdata) {
sd_dhcp_client *client = userdata;
DHCP_CLIENT_DONT_DESTROY(client);
log_dhcp_client(client, "EXPIRED");
- client_notify(client, DHCP_EVENT_EXPIRED);
+ client_notify(client, SD_DHCP_CLIENT_EVENT_EXPIRED);
/* lease was lost, start over if not freed or stopped in callback */
if (client->state != DHCP_STATE_STOPPED) {
@@ -1002,13 +1160,15 @@ static int client_timeout_t2(sd_event_source *s, uint64_t usec, void *userdata)
DHCP_CLIENT_DONT_DESTROY(client);
int r;
+ assert(client);
+
client->receive_message = sd_event_source_unref(client->receive_message);
client->fd = asynchronous_close(client->fd);
client->state = DHCP_STATE_REBINDING;
client->attempt = 1;
- r = dhcp_network_bind_raw_socket(client->index, &client->link,
+ r = dhcp_network_bind_raw_socket(client->ifindex, &client->link,
client->xid, client->mac_addr,
client->mac_addr_len, client->arp_type);
if (r < 0) {
@@ -1020,8 +1180,7 @@ static int client_timeout_t2(sd_event_source *s, uint64_t usec, void *userdata)
return client_initialize_events(client, client_receive_message_raw);
}
-static int client_timeout_t1(sd_event_source *s, uint64_t usec,
- void *userdata) {
+static int client_timeout_t1(sd_event_source *s, uint64_t usec, void *userdata) {
sd_dhcp_client *client = userdata;
DHCP_CLIENT_DONT_DESTROY(client);
@@ -1031,9 +1190,8 @@ static int client_timeout_t1(sd_event_source *s, uint64_t usec,
return client_initialize_time_events(client);
}
-static int client_handle_offer(sd_dhcp_client *client, DHCPMessage *offer,
- size_t len) {
- _cleanup_dhcp_lease_unref_ sd_dhcp_lease *lease = NULL;
+static int client_handle_offer(sd_dhcp_client *client, DHCPMessage *offer, size_t len) {
+ _cleanup_(sd_dhcp_lease_unrefp) sd_dhcp_lease *lease = NULL;
int r;
r = dhcp_lease_new(&lease);
@@ -1048,25 +1206,23 @@ static int client_handle_offer(sd_dhcp_client *client, DHCPMessage *offer,
return r;
}
- r = dhcp_option_parse(offer, len, dhcp_lease_parse_options, lease);
+ r = dhcp_option_parse(offer, len, dhcp_lease_parse_options, lease, NULL);
if (r != DHCP_OFFER) {
log_dhcp_client(client, "received message was not an OFFER, ignoring");
return -ENOMSG;
}
lease->next_server = offer->siaddr;
-
lease->address = offer->yiaddr;
- if (lease->address == INADDR_ANY ||
- lease->server_address == INADDR_ANY ||
+ if (lease->address == 0 ||
+ lease->server_address == 0 ||
lease->lifetime == 0) {
- log_dhcp_client(client, "received lease lacks address, server "
- "address or lease lifetime, ignoring");
+ log_dhcp_client(client, "received lease lacks address, server address or lease lifetime, ignoring");
return -ENOMSG;
}
- if (lease->subnet_mask == INADDR_ANY) {
+ if (!lease->have_subnet_mask) {
r = dhcp_lease_set_default_subnet_mask(lease);
if (r < 0) {
log_dhcp_client(client, "received lease lacks subnet "
@@ -1085,11 +1241,10 @@ static int client_handle_offer(sd_dhcp_client *client, DHCPMessage *offer,
return 0;
}
-static int client_handle_forcerenew(sd_dhcp_client *client, DHCPMessage *force,
- size_t len) {
+static int client_handle_forcerenew(sd_dhcp_client *client, DHCPMessage *force, size_t len) {
int r;
- r = dhcp_option_parse(force, len, NULL, NULL);
+ r = dhcp_option_parse(force, len, NULL, NULL, NULL);
if (r != DHCP_FORCERENEW)
return -ENOMSG;
@@ -1098,9 +1253,9 @@ static int client_handle_forcerenew(sd_dhcp_client *client, DHCPMessage *force,
return 0;
}
-static int client_handle_ack(sd_dhcp_client *client, DHCPMessage *ack,
- size_t len) {
- _cleanup_dhcp_lease_unref_ sd_dhcp_lease *lease = NULL;
+static int client_handle_ack(sd_dhcp_client *client, DHCPMessage *ack, size_t len) {
+ _cleanup_(sd_dhcp_lease_unrefp) sd_dhcp_lease *lease = NULL;
+ _cleanup_free_ char *error_message = NULL;
int r;
r = dhcp_lease_new(&lease);
@@ -1115,9 +1270,9 @@ static int client_handle_ack(sd_dhcp_client *client, DHCPMessage *ack,
return r;
}
- r = dhcp_option_parse(ack, len, dhcp_lease_parse_options, lease);
+ r = dhcp_option_parse(ack, len, dhcp_lease_parse_options, lease, &error_message);
if (r == DHCP_NAK) {
- log_dhcp_client(client, "NAK");
+ log_dhcp_client(client, "NAK: %s", strna(error_message));
return -EADDRNOTAVAIL;
}
@@ -1148,14 +1303,14 @@ static int client_handle_ack(sd_dhcp_client *client, DHCPMessage *ack,
}
}
- r = DHCP_EVENT_IP_ACQUIRE;
+ r = SD_DHCP_CLIENT_EVENT_IP_ACQUIRE;
if (client->lease) {
if (client->lease->address != lease->address ||
client->lease->subnet_mask != lease->subnet_mask ||
client->lease->router != lease->router) {
- r = DHCP_EVENT_IP_CHANGE;
+ r = SD_DHCP_CLIENT_EVENT_IP_CHANGE;
} else
- r = DHCP_EVENT_RENEW;
+ r = SD_DHCP_CLIENT_EVENT_RENEW;
client->lease = sd_dhcp_lease_unref(client->lease);
}
@@ -1168,13 +1323,17 @@ static int client_handle_ack(sd_dhcp_client *client, DHCPMessage *ack,
return r;
}
-static uint64_t client_compute_timeout(sd_dhcp_client *client,
- uint32_t lifetime, double factor) {
+static uint64_t client_compute_timeout(sd_dhcp_client *client, uint32_t lifetime, double factor) {
assert(client);
assert(client->request_sent);
- assert(lifetime);
+ assert(lifetime > 0);
+
+ if (lifetime > 3)
+ lifetime -= 3;
+ else
+ lifetime = 0;
- return client->request_sent + ((lifetime - 3) * USEC_PER_SEC * factor) +
+ return client->request_sent + (lifetime * USEC_PER_SEC * factor) +
+ (random_u32() & 0x1fffff);
}
@@ -1206,7 +1365,7 @@ static int client_set_lease_timeouts(sd_dhcp_client *client) {
/* convert the various timeouts from relative (secs) to absolute (usecs) */
lifetime_timeout = client_compute_timeout(client, client->lease->lifetime, 1);
- if (client->lease->t1 && client->lease->t2) {
+ if (client->lease->t1 > 0 && client->lease->t2 > 0) {
/* both T1 and T2 are given */
if (client->lease->t1 < client->lease->t2 &&
client->lease->t2 < client->lease->lifetime) {
@@ -1220,7 +1379,7 @@ static int client_set_lease_timeouts(sd_dhcp_client *client) {
t1_timeout = client_compute_timeout(client, client->lease->lifetime, 0.5);
client->lease->t1 = client->lease->lifetime / 2;
}
- } else if (client->lease->t2 && client->lease->t2 < client->lease->lifetime) {
+ } else if (client->lease->t2 > 0 && client->lease->t2 < client->lease->lifetime) {
/* only T2 is given, and it is valid */
t2_timeout = client_compute_timeout(client, client->lease->t2, 1);
t1_timeout = client_compute_timeout(client, client->lease->lifetime, 0.5);
@@ -1230,7 +1389,7 @@ static int client_set_lease_timeouts(sd_dhcp_client *client) {
t2_timeout = client_compute_timeout(client, client->lease->lifetime, 7.0 / 8.0);
client->lease->t2 = (client->lease->lifetime * 7) / 8;
}
- } else if (client->lease->t1 && client->lease->t1 < client->lease->lifetime) {
+ } else if (client->lease->t1 > 0 && client->lease->t1 < client->lease->lifetime) {
/* only T1 is given, and it is valid */
t1_timeout = client_compute_timeout(client, client->lease->t1, 1);
t2_timeout = client_compute_timeout(client, client->lease->lifetime, 7.0 / 8.0);
@@ -1266,8 +1425,7 @@ static int client_set_lease_timeouts(sd_dhcp_client *client) {
return r;
log_dhcp_client(client, "lease expires in %s",
- format_timespan(time_string, FORMAT_TIMESPAN_MAX,
- lifetime_timeout - time_now, 0));
+ format_timespan(time_string, FORMAT_TIMESPAN_MAX, lifetime_timeout - time_now, USEC_PER_SEC));
/* don't arm earlier timeouts if this has already expired */
if (lifetime_timeout <= time_now)
@@ -1293,8 +1451,7 @@ static int client_set_lease_timeouts(sd_dhcp_client *client) {
return r;
log_dhcp_client(client, "T2 expires in %s",
- format_timespan(time_string, FORMAT_TIMESPAN_MAX,
- t2_timeout - time_now, 0));
+ format_timespan(time_string, FORMAT_TIMESPAN_MAX, t2_timeout - time_now, USEC_PER_SEC));
/* don't arm earlier timeout if this has already expired */
if (t2_timeout <= time_now)
@@ -1319,15 +1476,14 @@ static int client_set_lease_timeouts(sd_dhcp_client *client) {
return r;
log_dhcp_client(client, "T1 expires in %s",
- format_timespan(time_string, FORMAT_TIMESPAN_MAX,
- t1_timeout - time_now, 0));
+ format_timespan(time_string, FORMAT_TIMESPAN_MAX, t1_timeout - time_now, USEC_PER_SEC));
return 0;
}
-static int client_handle_message(sd_dhcp_client *client, DHCPMessage *message,
- int len) {
+static int client_handle_message(sd_dhcp_client *client, DHCPMessage *message, int len) {
DHCP_CLIENT_DONT_DESTROY(client);
+ char time_string[FORMAT_TIMESPAN_MAX];
int r = 0, notify_event = 0;
assert(client);
@@ -1375,6 +1531,7 @@ static int client_handle_message(sd_dhcp_client *client, DHCPMessage *message,
r = client_handle_ack(client, message, len);
if (r >= 0) {
+ client->start_delay = 0;
client->timeout_resend =
sd_event_source_unref(client->timeout_resend);
client->receive_message =
@@ -1383,8 +1540,8 @@ static int client_handle_message(sd_dhcp_client *client, DHCPMessage *message,
if (IN_SET(client->state, DHCP_STATE_REQUESTING,
DHCP_STATE_REBOOTING))
- notify_event = DHCP_EVENT_IP_ACQUIRE;
- else if (r != DHCP_EVENT_IP_ACQUIRE)
+ notify_event = SD_DHCP_CLIENT_EVENT_IP_ACQUIRE;
+ else if (r != SD_DHCP_CLIENT_EVENT_IP_ACQUIRE)
notify_event = r;
client->state = DHCP_STATE_BOUND;
@@ -1424,11 +1581,15 @@ static int client_handle_message(sd_dhcp_client *client, DHCPMessage *message,
if (r < 0)
goto error;
- r = client_start(client);
+ r = client_start_delayed(client);
if (r < 0)
goto error;
- log_dhcp_client(client, "REBOOTED");
+ log_dhcp_client(client, "REBOOT in %s", format_timespan(time_string, FORMAT_TIMESPAN_MAX,
+ client->start_delay, USEC_PER_SEC));
+
+ client->start_delay = CLAMP(client->start_delay * 2,
+ RESTART_AFTER_NAK_MIN_USEC, RESTART_AFTER_NAK_MAX_USEC);
return 0;
} else if (r == -ENOMSG)
@@ -1466,52 +1627,54 @@ error:
return r;
}
-static int client_receive_message_udp(sd_event_source *s, int fd,
- uint32_t revents, void *userdata) {
+static int client_receive_message_udp(
+ sd_event_source *s,
+ int fd,
+ uint32_t revents,
+ void *userdata) {
+
sd_dhcp_client *client = userdata;
_cleanup_free_ DHCPMessage *message = NULL;
- int buflen = 0, len, r;
- const struct ether_addr zero_mac = { { 0, 0, 0, 0, 0, 0 } };
+ const struct ether_addr zero_mac = {};
const struct ether_addr *expected_chaddr = NULL;
uint8_t expected_hlen = 0;
+ ssize_t len, buflen;
assert(s);
assert(client);
- r = ioctl(fd, FIONREAD, &buflen);
- if (r < 0)
- return r;
-
+ buflen = next_datagram_size_fd(fd);
if (buflen < 0)
- /* this can't be right */
- return -EIO;
+ return buflen;
message = malloc0(buflen);
if (!message)
return -ENOMEM;
- len = read(fd, message, buflen);
+ len = recv(fd, message, buflen, 0);
if (len < 0) {
- log_dhcp_client(client, "could not receive message from UDP "
- "socket: %m");
- return 0;
- } else if ((size_t)len < sizeof(DHCPMessage)) {
- log_dhcp_client(client, "too small to be a DHCP message: ignoring");
+ if (errno == EAGAIN || errno == EINTR)
+ return 0;
+
+ return log_dhcp_client_errno(client, errno, "Could not receive message from UDP socket: %m");
+ }
+ if ((size_t) len < sizeof(DHCPMessage)) {
+ log_dhcp_client(client, "Too small to be a DHCP message: ignoring");
return 0;
}
if (be32toh(message->magic) != DHCP_MAGIC_COOKIE) {
- log_dhcp_client(client, "not a DHCP message: ignoring");
+ log_dhcp_client(client, "Not a DHCP message: ignoring");
return 0;
}
if (message->op != BOOTREPLY) {
- log_dhcp_client(client, "not a BOOTREPLY message: ignoring");
+ log_dhcp_client(client, "Not a BOOTREPLY message: ignoring");
return 0;
}
if (message->htype != client->arp_type) {
- log_dhcp_client(client, "packet type does not match client type");
+ log_dhcp_client(client, "Packet type does not match client type");
return 0;
}
@@ -1519,19 +1682,18 @@ static int client_receive_message_udp(sd_event_source *s, int fd,
expected_hlen = ETH_ALEN;
expected_chaddr = (const struct ether_addr *) &client->mac_addr;
} else {
- /* Non-ethernet links expect zero chaddr */
+ /* Non-Ethernet links expect zero chaddr */
expected_hlen = 0;
expected_chaddr = &zero_mac;
}
if (message->hlen != expected_hlen) {
- log_dhcp_client(client, "unexpected packet hlen %d", message->hlen);
+ log_dhcp_client(client, "Unexpected packet hlen %d", message->hlen);
return 0;
}
if (memcmp(&message->chaddr[0], expected_chaddr, ETH_ALEN)) {
- log_dhcp_client(client, "received chaddr does not match "
- "expected: ignoring");
+ log_dhcp_client(client, "Received chaddr does not match expected: ignoring");
return 0;
}
@@ -1539,8 +1701,7 @@ static int client_receive_message_udp(sd_event_source *s, int fd,
be32toh(message->xid) != client->xid) {
/* in BOUND state, we may receive FORCERENEW with xid set by server,
so ignore the xid in this case */
- log_dhcp_client(client, "received xid (%u) does not match "
- "expected (%u): ignoring",
+ log_dhcp_client(client, "Received xid (%u) does not match expected (%u): ignoring",
be32toh(message->xid), client->xid);
return 0;
}
@@ -1548,8 +1709,12 @@ static int client_receive_message_udp(sd_event_source *s, int fd,
return client_handle_message(client, message, len);
}
-static int client_receive_message_raw(sd_event_source *s, int fd,
- uint32_t revents, void *userdata) {
+static int client_receive_message_raw(
+ sd_event_source *s,
+ int fd,
+ uint32_t revents,
+ void *userdata) {
+
sd_dhcp_client *client = userdata;
_cleanup_free_ DHCPPacket *packet = NULL;
uint8_t cmsgbuf[CMSG_LEN(sizeof(struct tpacket_auxdata))];
@@ -1562,18 +1727,15 @@ static int client_receive_message_raw(sd_event_source *s, int fd,
};
struct cmsghdr *cmsg;
bool checksum = true;
- int buflen = 0, len, r;
+ ssize_t buflen, len;
+ int r;
assert(s);
assert(client);
- r = ioctl(fd, FIONREAD, &buflen);
- if (r < 0)
- return r;
-
+ buflen = next_datagram_size_fd(fd);
if (buflen < 0)
- /* this can't be right */
- return -EIO;
+ return buflen;
packet = malloc0(buflen);
if (!packet)
@@ -1584,9 +1746,12 @@ static int client_receive_message_raw(sd_event_source *s, int fd,
len = recvmsg(fd, &msg, 0);
if (len < 0) {
- log_dhcp_client(client, "could not receive message from raw "
- "socket: %m");
- return 0;
+ if (errno == EAGAIN || errno == EINTR)
+ return 0;
+
+ log_dhcp_client(client, "Could not receive message from raw socket: %m");
+
+ return -errno;
} else if ((size_t)len < sizeof(DHCPPacket))
return 0;
@@ -1624,7 +1789,7 @@ int sd_dhcp_client_start(sd_dhcp_client *client) {
r = client_start(client);
if (r >= 0)
- log_dhcp_client(client, "STARTED on ifindex %i", client->index);
+ log_dhcp_client(client, "STARTED on ifindex %i", client->ifindex);
return r;
}
@@ -1634,14 +1799,13 @@ int sd_dhcp_client_stop(sd_dhcp_client *client) {
assert_return(client, -EINVAL);
- client_stop(client, DHCP_EVENT_STOP);
+ client_stop(client, SD_DHCP_CLIENT_EVENT_STOP);
client->state = DHCP_STATE_STOPPED;
return 0;
}
-int sd_dhcp_client_attach_event(sd_dhcp_client *client, sd_event *event,
- int priority) {
+int sd_dhcp_client_attach_event(sd_dhcp_client *client, sd_event *event, int64_t priority) {
int r;
assert_return(client, -EINVAL);
@@ -1669,43 +1833,53 @@ int sd_dhcp_client_detach_event(sd_dhcp_client *client) {
}
sd_event *sd_dhcp_client_get_event(sd_dhcp_client *client) {
- if (!client)
- return NULL;
+ assert_return(client, NULL);
return client->event;
}
sd_dhcp_client *sd_dhcp_client_ref(sd_dhcp_client *client) {
- if (client)
- assert_se(REFCNT_INC(client->n_ref) >= 2);
+
+ if (!client)
+ return NULL;
+
+ assert(client->n_ref >= 1);
+ client->n_ref++;
return client;
}
sd_dhcp_client *sd_dhcp_client_unref(sd_dhcp_client *client) {
- if (client && REFCNT_DEC(client->n_ref) == 0) {
- log_dhcp_client(client, "FREE");
- client_initialize(client);
+ if (!client)
+ return NULL;
- client->receive_message =
- sd_event_source_unref(client->receive_message);
+ assert(client->n_ref >= 1);
+ client->n_ref--;
- sd_dhcp_client_detach_event(client);
+ if (client->n_ref > 0)
+ return NULL;
- sd_dhcp_lease_unref(client->lease);
+ log_dhcp_client(client, "FREE");
- free(client->req_opts);
- free(client->hostname);
- free(client->vendor_class_identifier);
- free(client);
- }
+ client_initialize(client);
+
+ client->receive_message = sd_event_source_unref(client->receive_message);
+
+ sd_dhcp_client_detach_event(client);
+
+ sd_dhcp_lease_unref(client->lease);
+
+ free(client->req_opts);
+ free(client->hostname);
+ free(client->vendor_class_identifier);
+ free(client);
return NULL;
}
int sd_dhcp_client_new(sd_dhcp_client **ret) {
- _cleanup_dhcp_client_unref_ sd_dhcp_client *client = NULL;
+ _cleanup_(sd_dhcp_client_unrefp) sd_dhcp_client *client = NULL;
assert_return(ret, -EINVAL);
@@ -1713,15 +1887,14 @@ int sd_dhcp_client_new(sd_dhcp_client **ret) {
if (!client)
return -ENOMEM;
- client->n_ref = REFCNT_INIT;
+ client->n_ref = 1;
client->state = DHCP_STATE_INIT;
- client->index = -1;
+ client->ifindex = -1;
client->fd = -1;
client->attempt = 1;
client->mtu = DHCP_DEFAULT_MIN_SIZE;
client->req_opts_size = ELEMENTSOF(default_req_opts);
-
client->req_opts = memdup(default_req_opts, client->req_opts_size);
if (!client->req_opts)
return -ENOMEM;
diff --git a/src/libsystemd-network/sd-dhcp-lease.c b/src/libsystemd-network/sd-dhcp-lease.c
index 54417b3af3..ef50ed17a1 100644
--- a/src/libsystemd-network/sd-dhcp-lease.c
+++ b/src/libsystemd-network/sd-dhcp-lease.c
@@ -18,28 +18,48 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <stdlib.h>
+#include <arpa/inet.h>
#include <errno.h>
-#include <string.h>
#include <stdio.h>
-#include <arpa/inet.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "sd-dhcp-lease.h"
+#include "alloc-util.h"
+#include "dhcp-lease-internal.h"
+#include "dhcp-protocol.h"
+#include "dns-domain.h"
+#include "fd-util.h"
#include "fileio.h"
-#include "unaligned.h"
-#include "in-addr-util.h"
+#include "hexdecoct.h"
#include "hostname-util.h"
-#include "dhcp-protocol.h"
-#include "dhcp-lease-internal.h"
-#include "sd-dhcp-lease.h"
+#include "in-addr-util.h"
#include "network-internal.h"
-#include "dns-domain.h"
+#include "parse-util.h"
+#include "stdio-util.h"
+#include "string-util.h"
+#include "unaligned.h"
int sd_dhcp_lease_get_address(sd_dhcp_lease *lease, struct in_addr *addr) {
assert_return(lease, -EINVAL);
assert_return(addr, -EINVAL);
+ if (lease->address == 0)
+ return -ENODATA;
+
addr->s_addr = lease->address;
+ return 0;
+}
+int sd_dhcp_lease_get_broadcast(sd_dhcp_lease *lease, struct in_addr *addr) {
+ assert_return(lease, -EINVAL);
+ assert_return(addr, -EINVAL);
+
+ if (!lease->have_broadcast)
+ return -ENODATA;
+
+ addr->s_addr = lease->broadcast;
return 0;
}
@@ -47,8 +67,32 @@ int sd_dhcp_lease_get_lifetime(sd_dhcp_lease *lease, uint32_t *lifetime) {
assert_return(lease, -EINVAL);
assert_return(lifetime, -EINVAL);
+ if (lease->lifetime <= 0)
+ return -ENODATA;
+
*lifetime = lease->lifetime;
+ return 0;
+}
+
+int sd_dhcp_lease_get_t1(sd_dhcp_lease *lease, uint32_t *t1) {
+ assert_return(lease, -EINVAL);
+ assert_return(t1, -EINVAL);
+
+ if (lease->t1 <= 0)
+ return -ENODATA;
+
+ *t1 = lease->t1;
+ return 0;
+}
+
+int sd_dhcp_lease_get_t2(sd_dhcp_lease *lease, uint32_t *t2) {
+ assert_return(lease, -EINVAL);
+ assert_return(t2, -EINVAL);
+ if (lease->t2 <= 0)
+ return -ENODATA;
+
+ *t2 = lease->t2;
return 0;
}
@@ -56,11 +100,10 @@ int sd_dhcp_lease_get_mtu(sd_dhcp_lease *lease, uint16_t *mtu) {
assert_return(lease, -EINVAL);
assert_return(mtu, -EINVAL);
- if (lease->mtu)
- *mtu = lease->mtu;
- else
- return -ENOENT;
+ if (lease->mtu <= 0)
+ return -ENODATA;
+ *mtu = lease->mtu;
return 0;
}
@@ -68,37 +111,32 @@ int sd_dhcp_lease_get_dns(sd_dhcp_lease *lease, const struct in_addr **addr) {
assert_return(lease, -EINVAL);
assert_return(addr, -EINVAL);
- if (lease->dns_size) {
- *addr = lease->dns;
- return lease->dns_size;
- } else
- return -ENOENT;
+ if (lease->dns_size <= 0)
+ return -ENODATA;
- return 0;
+ *addr = lease->dns;
+ return (int) lease->dns_size;
}
int sd_dhcp_lease_get_ntp(sd_dhcp_lease *lease, const struct in_addr **addr) {
assert_return(lease, -EINVAL);
assert_return(addr, -EINVAL);
- if (lease->ntp_size) {
- *addr = lease->ntp;
- return lease->ntp_size;
- } else
- return -ENOENT;
+ if (lease->ntp_size <= 0)
+ return -ENODATA;
- return 0;
+ *addr = lease->ntp;
+ return (int) lease->ntp_size;
}
int sd_dhcp_lease_get_domainname(sd_dhcp_lease *lease, const char **domainname) {
assert_return(lease, -EINVAL);
assert_return(domainname, -EINVAL);
- if (lease->domainname)
- *domainname = lease->domainname;
- else
- return -ENOENT;
+ if (!lease->domainname)
+ return -ENODATA;
+ *domainname = lease->domainname;
return 0;
}
@@ -106,11 +144,10 @@ int sd_dhcp_lease_get_hostname(sd_dhcp_lease *lease, const char **hostname) {
assert_return(lease, -EINVAL);
assert_return(hostname, -EINVAL);
- if (lease->hostname)
- *hostname = lease->hostname;
- else
- return -ENOENT;
+ if (!lease->hostname)
+ return -ENODATA;
+ *hostname = lease->hostname;
return 0;
}
@@ -118,11 +155,10 @@ int sd_dhcp_lease_get_root_path(sd_dhcp_lease *lease, const char **root_path) {
assert_return(lease, -EINVAL);
assert_return(root_path, -EINVAL);
- if (lease->root_path)
- *root_path = lease->root_path;
- else
- return -ENOENT;
+ if (!lease->root_path)
+ return -ENODATA;
+ *root_path = lease->root_path;
return 0;
}
@@ -130,11 +166,10 @@ int sd_dhcp_lease_get_router(sd_dhcp_lease *lease, struct in_addr *addr) {
assert_return(lease, -EINVAL);
assert_return(addr, -EINVAL);
- if (lease->router != INADDR_ANY)
- addr->s_addr = lease->router;
- else
- return -ENOENT;
+ if (lease->router == 0)
+ return -ENODATA;
+ addr->s_addr = lease->router;
return 0;
}
@@ -142,8 +177,10 @@ int sd_dhcp_lease_get_netmask(sd_dhcp_lease *lease, struct in_addr *addr) {
assert_return(lease, -EINVAL);
assert_return(addr, -EINVAL);
- addr->s_addr = lease->subnet_mask;
+ if (!lease->have_subnet_mask)
+ return -ENODATA;
+ addr->s_addr = lease->subnet_mask;
return 0;
}
@@ -151,8 +188,10 @@ int sd_dhcp_lease_get_server_identifier(sd_dhcp_lease *lease, struct in_addr *ad
assert_return(lease, -EINVAL);
assert_return(addr, -EINVAL);
- addr->s_addr = lease->server_address;
+ if (lease->server_address == 0)
+ return -ENODATA;
+ addr->s_addr = lease->server_address;
return 0;
}
@@ -160,128 +199,152 @@ int sd_dhcp_lease_get_next_server(sd_dhcp_lease *lease, struct in_addr *addr) {
assert_return(lease, -EINVAL);
assert_return(addr, -EINVAL);
- addr->s_addr = lease->next_server;
+ if (lease->next_server == 0)
+ return -ENODATA;
+ addr->s_addr = lease->next_server;
return 0;
}
-int sd_dhcp_lease_get_routes(sd_dhcp_lease *lease, struct sd_dhcp_route **routes) {
+/*
+ * The returned routes array must be freed by the caller.
+ * Route objects have the same lifetime of the lease and must not be freed.
+ */
+int sd_dhcp_lease_get_routes(sd_dhcp_lease *lease, sd_dhcp_route ***routes) {
+ sd_dhcp_route **ret;
+ unsigned i;
assert_return(lease, -EINVAL);
assert_return(routes, -EINVAL);
- if (lease->static_route_size) {
- *routes = lease->static_route;
- return lease->static_route_size;
- } else
- return -ENOENT;
+ if (lease->static_route_size <= 0)
+ return -ENODATA;
- return 0;
+ ret = new(sd_dhcp_route *, lease->static_route_size);
+ if (!ret)
+ return -ENOMEM;
+
+ for (i = 0; i < lease->static_route_size; i++)
+ ret[i] = &lease->static_route[i];
+
+ *routes = ret;
+ return (int) lease->static_route_size;
}
-int sd_dhcp_lease_get_vendor_specific(sd_dhcp_lease *lease, const uint8_t **data,
- size_t *data_len) {
+int sd_dhcp_lease_get_vendor_specific(sd_dhcp_lease *lease, const void **data, size_t *data_len) {
assert_return(lease, -EINVAL);
assert_return(data, -EINVAL);
assert_return(data_len, -EINVAL);
- if (!lease->vendor_specific)
- return -ENOENT;
+ if (lease->vendor_specific_len <= 0)
+ return -ENODATA;
*data = lease->vendor_specific;
*data_len = lease->vendor_specific_len;
-
return 0;
}
sd_dhcp_lease *sd_dhcp_lease_ref(sd_dhcp_lease *lease) {
- if (lease)
- assert_se(REFCNT_INC(lease->n_ref) >= 2);
+
+ if (!lease)
+ return NULL;
+
+ assert(lease->n_ref >= 1);
+ lease->n_ref++;
return lease;
}
sd_dhcp_lease *sd_dhcp_lease_unref(sd_dhcp_lease *lease) {
- if (lease && REFCNT_DEC(lease->n_ref) == 0) {
- free(lease->hostname);
- free(lease->domainname);
- free(lease->dns);
- free(lease->ntp);
- free(lease->static_route);
- free(lease->client_id);
- free(lease->vendor_specific);
- free(lease);
- }
- return NULL;
-}
+ if (!lease)
+ return NULL;
-static void lease_parse_u32(const uint8_t *option, size_t len, uint32_t *ret, uint32_t min) {
- assert(option);
- assert(ret);
+ assert(lease->n_ref >= 1);
+ lease->n_ref--;
- if (len == 4) {
- *ret = unaligned_read_be32((be32_t*) option);
+ if (lease->n_ref > 0)
+ return NULL;
- if (*ret < min)
- *ret = min;
+ while (lease->private_options) {
+ struct sd_dhcp_raw_option *option = lease->private_options;
+
+ LIST_REMOVE(options, lease->private_options, option);
+
+ free(option->data);
+ free(option);
}
-}
-static void lease_parse_s32(const uint8_t *option, size_t len, int32_t *ret) {
- lease_parse_u32(option, len, (uint32_t *)ret, 0);
+ free(lease->hostname);
+ free(lease->domainname);
+ free(lease->dns);
+ free(lease->ntp);
+ free(lease->static_route);
+ free(lease->client_id);
+ free(lease->vendor_specific);
+ free(lease);
+
+ return NULL;
}
-static void lease_parse_u16(const uint8_t *option, size_t len, uint16_t *ret, uint16_t min) {
+static int lease_parse_u32(const uint8_t *option, size_t len, uint32_t *ret, uint32_t min) {
assert(option);
assert(ret);
- if (len == 2) {
- *ret = unaligned_read_be16((be16_t*) option);
+ if (len != 4)
+ return -EINVAL;
- if (*ret < min)
- *ret = min;
- }
+ *ret = unaligned_read_be32((be32_t*) option);
+ if (*ret < min)
+ *ret = min;
+
+ return 0;
}
-static void lease_parse_be32(const uint8_t *option, size_t len, be32_t *ret) {
+static int lease_parse_u16(const uint8_t *option, size_t len, uint16_t *ret, uint16_t min) {
assert(option);
assert(ret);
- if (len == 4)
- memcpy(ret, option, 4);
-}
+ if (len != 2)
+ return -EINVAL;
-static void lease_parse_bool(const uint8_t *option, size_t len, bool *ret) {
- assert(option);
- assert(ret);
+ *ret = unaligned_read_be16((be16_t*) option);
+ if (*ret < min)
+ *ret = min;
- if (len == 1)
- *ret = !!(*option);
+ return 0;
}
-static void lease_parse_u8(const uint8_t *option, size_t len, uint8_t *ret, uint8_t min) {
+static int lease_parse_be32(const uint8_t *option, size_t len, be32_t *ret) {
assert(option);
assert(ret);
- if (len == 1) {
- *ret = *option;
+ if (len != 4)
+ return -EINVAL;
- if (*ret < min)
- *ret = min;
- }
+ memcpy(ret, option, 4);
+ return 0;
}
static int lease_parse_string(const uint8_t *option, size_t len, char **ret) {
assert(option);
assert(ret);
- if (len >= 1) {
+ if (len <= 0)
+ *ret = mfree(*ret);
+ else {
char *string;
- string = strndup((const char *)option, len);
+ /*
+ * One trailing NUL byte is OK, we don't mind. See:
+ * https://github.com/systemd/systemd/issues/1337
+ */
+ if (memchr(option, 0, len - 1))
+ return -EINVAL;
+
+ string = strndup((const char *) option, len);
if (!string)
- return -errno;
+ return -ENOMEM;
free(*ret);
*ret = string;
@@ -290,48 +353,79 @@ static int lease_parse_string(const uint8_t *option, size_t len, char **ret) {
return 0;
}
-static int lease_parse_in_addrs_aux(const uint8_t *option, size_t len, struct in_addr **ret, size_t *ret_size, size_t mult) {
+static int lease_parse_domain(const uint8_t *option, size_t len, char **ret) {
+ _cleanup_free_ char *name = NULL, *normalized = NULL;
+ int r;
+
assert(option);
assert(ret);
- assert(ret_size);
- if (len && !(len % (4 * mult))) {
- size_t size;
+ r = lease_parse_string(option, len, &name);
+ if (r < 0)
+ return r;
+ if (!name) {
+ *ret = mfree(*ret);
+ return 0;
+ }
+
+ r = dns_name_normalize(name, &normalized);
+ if (r < 0)
+ return r;
+
+ if (is_localhost(normalized))
+ return -EINVAL;
+
+ if (dns_name_is_root(normalized))
+ return -EINVAL;
+
+ free(*ret);
+ *ret = normalized;
+ normalized = NULL;
+
+ return 0;
+}
+
+static int lease_parse_in_addrs(const uint8_t *option, size_t len, struct in_addr **ret, size_t *n_ret) {
+ assert(option);
+ assert(ret);
+ assert(n_ret);
+
+ if (len <= 0) {
+ *ret = mfree(*ret);
+ *n_ret = 0;
+ } else {
+ size_t n_addresses;
struct in_addr *addresses;
- size = len / 4;
+ if (len % 4 != 0)
+ return -EINVAL;
+
+ n_addresses = len / 4;
- addresses = newdup(struct in_addr, option, size);
+ addresses = newdup(struct in_addr, option, n_addresses);
if (!addresses)
return -ENOMEM;
free(*ret);
*ret = addresses;
- *ret_size = size;
+ *n_ret = n_addresses;
}
return 0;
}
-static int lease_parse_in_addrs(const uint8_t *option, size_t len, struct in_addr **ret, size_t *ret_size) {
- return lease_parse_in_addrs_aux(option, len, ret, ret_size, 1);
-}
-
-static int lease_parse_in_addrs_pairs(const uint8_t *option, size_t len, struct in_addr **ret, size_t *ret_size) {
- return lease_parse_in_addrs_aux(option, len, ret, ret_size, 2);
-}
-
-static int lease_parse_routes(const uint8_t *option, size_t len, struct sd_dhcp_route **routes,
- size_t *routes_size, size_t *routes_allocated) {
+static int lease_parse_routes(
+ const uint8_t *option, size_t len,
+ struct sd_dhcp_route **routes, size_t *routes_size, size_t *routes_allocated) {
struct in_addr addr;
- assert(option);
+ assert(option || len <= 0);
assert(routes);
assert(routes_size);
assert(routes_allocated);
- if (!len)
+ if (len <= 0)
return 0;
if (len % 8 != 0)
@@ -346,15 +440,15 @@ static int lease_parse_routes(const uint8_t *option, size_t len, struct sd_dhcp_
r = in_addr_default_prefixlen((struct in_addr*) option, &route->dst_prefixlen);
if (r < 0) {
- log_error("Failed to determine destination prefix length from class based IP, ignoring");
+ log_debug("Failed to determine destination prefix length from class based IP, ignoring");
continue;
}
- lease_parse_be32(option, 4, &addr.s_addr);
+ assert_se(lease_parse_be32(option, 4, &addr.s_addr) >= 0);
route->dst_addr = inet_makeaddr(inet_netof(addr), 0);
option += 4;
- lease_parse_be32(option, 4, &route->gw_addr.s_addr);
+ assert_se(lease_parse_be32(option, 4, &route->gw_addr.s_addr) >= 0);
option += 4;
len -= 8;
@@ -365,14 +459,18 @@ static int lease_parse_routes(const uint8_t *option, size_t len, struct sd_dhcp_
}
/* parses RFC3442 Classless Static Route Option */
-static int lease_parse_classless_routes(const uint8_t *option, size_t len, struct sd_dhcp_route **routes,
- size_t *routes_size, size_t *routes_allocated) {
+static int lease_parse_classless_routes(
+ const uint8_t *option, size_t len,
+ struct sd_dhcp_route **routes, size_t *routes_size, size_t *routes_allocated) {
- assert(option);
+ assert(option || len <= 0);
assert(routes);
assert(routes_size);
assert(routes_allocated);
+ if (len <= 0)
+ return 0;
+
/* option format: (subnet-mask-width significant-subnet-octets gateway-ip)* */
while (len > 0) {
@@ -380,7 +478,7 @@ static int lease_parse_classless_routes(const uint8_t *option, size_t len, struc
struct sd_dhcp_route *route;
if (!GREEDY_REALLOC(*routes, *routes_allocated, *routes_size + 1))
- return -ENOMEM;
+ return -ENOMEM;
route = *routes + *routes_size;
@@ -401,7 +499,7 @@ static int lease_parse_classless_routes(const uint8_t *option, size_t len, struc
if (len < 4)
return -EINVAL;
- lease_parse_be32(option, 4, &route->gw_addr.s_addr);
+ assert_se(lease_parse_be32(option, 4, &route->gw_addr.s_addr) >= 0);
option += 4;
len -= 4;
@@ -411,204 +509,203 @@ static int lease_parse_classless_routes(const uint8_t *option, size_t len, struc
return 0;
}
-int dhcp_lease_parse_options(uint8_t code, uint8_t len, const uint8_t *option,
- void *user_data) {
- sd_dhcp_lease *lease = user_data;
+int dhcp_lease_parse_options(uint8_t code, uint8_t len, const void *option, void *userdata) {
+ sd_dhcp_lease *lease = userdata;
int r;
assert(lease);
switch(code) {
- case DHCP_OPTION_TIME_OFFSET:
- lease_parse_s32(option, len, &lease->time_offset);
-
- break;
-
- case DHCP_OPTION_INTERFACE_MTU_AGING_TIMEOUT:
- lease_parse_u32(option, len, &lease->mtu_aging_timeout, 0);
-
- break;
-
- case DHCP_OPTION_IP_ADDRESS_LEASE_TIME:
- lease_parse_u32(option, len, &lease->lifetime, 1);
+ case SD_DHCP_OPTION_IP_ADDRESS_LEASE_TIME:
+ r = lease_parse_u32(option, len, &lease->lifetime, 1);
+ if (r < 0)
+ log_debug_errno(r, "Failed to parse lease time, ignoring: %m");
break;
- case DHCP_OPTION_SERVER_IDENTIFIER:
- lease_parse_be32(option, len, &lease->server_address);
+ case SD_DHCP_OPTION_SERVER_IDENTIFIER:
+ r = lease_parse_be32(option, len, &lease->server_address);
+ if (r < 0)
+ log_debug_errno(r, "Failed to parse server identifier, ignoring: %m");
break;
- case DHCP_OPTION_SUBNET_MASK:
- lease_parse_be32(option, len, &lease->subnet_mask);
-
+ case SD_DHCP_OPTION_SUBNET_MASK:
+ r = lease_parse_be32(option, len, &lease->subnet_mask);
+ if (r < 0)
+ log_debug_errno(r, "Failed to parse subnet mask, ignoring: %m");
+ else
+ lease->have_subnet_mask = true;
break;
- case DHCP_OPTION_BROADCAST:
- lease_parse_be32(option, len, &lease->broadcast);
-
+ case SD_DHCP_OPTION_BROADCAST:
+ r = lease_parse_be32(option, len, &lease->broadcast);
+ if (r < 0)
+ log_debug_errno(r, "Failed to parse broadcast address, ignoring: %m");
+ else
+ lease->have_broadcast = true;
break;
- case DHCP_OPTION_ROUTER:
- if(len >= 4)
- lease_parse_be32(option, 4, &lease->router);
-
+ case SD_DHCP_OPTION_ROUTER:
+ if (len >= 4) {
+ r = lease_parse_be32(option, 4, &lease->router);
+ if (r < 0)
+ log_debug_errno(r, "Failed to parse router address, ignoring: %m");
+ }
break;
- case DHCP_OPTION_DOMAIN_NAME_SERVER:
+ case SD_DHCP_OPTION_DOMAIN_NAME_SERVER:
r = lease_parse_in_addrs(option, len, &lease->dns, &lease->dns_size);
if (r < 0)
- return r;
-
+ log_debug_errno(r, "Failed to parse DNS server, ignoring: %m");
break;
- case DHCP_OPTION_NTP_SERVER:
+ case SD_DHCP_OPTION_NTP_SERVER:
r = lease_parse_in_addrs(option, len, &lease->ntp, &lease->ntp_size);
if (r < 0)
- return r;
-
+ log_debug_errno(r, "Failed to parse NTP server, ignoring: %m");
break;
- case DHCP_OPTION_POLICY_FILTER:
- r = lease_parse_in_addrs_pairs(option, len, &lease->policy_filter, &lease->policy_filter_size);
+ case SD_DHCP_OPTION_STATIC_ROUTE:
+ r = lease_parse_routes(option, len, &lease->static_route, &lease->static_route_size, &lease->static_route_allocated);
if (r < 0)
- return r;
-
+ log_debug_errno(r, "Failed to parse static routes, ignoring: %m");
break;
- case DHCP_OPTION_STATIC_ROUTE:
- r = lease_parse_routes(option, len, &lease->static_route, &lease->static_route_size,
- &lease->static_route_allocated);
+ case SD_DHCP_OPTION_INTERFACE_MTU:
+ r = lease_parse_u16(option, len, &lease->mtu, 68);
if (r < 0)
- return r;
-
+ log_debug_errno(r, "Failed to parse MTU, ignoring: %m");
break;
- case DHCP_OPTION_INTERFACE_MTU:
- lease_parse_u16(option, len, &lease->mtu, 68);
+ case SD_DHCP_OPTION_DOMAIN_NAME:
+ r = lease_parse_domain(option, len, &lease->domainname);
+ if (r < 0) {
+ log_debug_errno(r, "Failed to parse domain name, ignoring: %m");
+ return 0;
+ }
break;
- case DHCP_OPTION_INTERFACE_MDR:
- lease_parse_u16(option, len, &lease->mdr, 576);
+ case SD_DHCP_OPTION_HOST_NAME:
+ r = lease_parse_domain(option, len, &lease->hostname);
+ if (r < 0) {
+ log_debug_errno(r, "Failed to parse host name, ignoring: %m");
+ return 0;
+ }
break;
- case DHCP_OPTION_INTERFACE_TTL:
- lease_parse_u8(option, len, &lease->ttl, 1);
-
+ case SD_DHCP_OPTION_ROOT_PATH:
+ r = lease_parse_string(option, len, &lease->root_path);
+ if (r < 0)
+ log_debug_errno(r, "Failed to parse root path, ignoring: %m");
break;
- case DHCP_OPTION_BOOT_FILE_SIZE:
- lease_parse_u16(option, len, &lease->boot_file_size, 0);
-
+ case SD_DHCP_OPTION_RENEWAL_T1_TIME:
+ r = lease_parse_u32(option, len, &lease->t1, 1);
+ if (r < 0)
+ log_debug_errno(r, "Failed to parse T1 time, ignoring: %m");
break;
- case DHCP_OPTION_DOMAIN_NAME:
- {
- _cleanup_free_ char *domainname = NULL;
- char *e;
+ case SD_DHCP_OPTION_REBINDING_T2_TIME:
+ r = lease_parse_u32(option, len, &lease->t2, 1);
+ if (r < 0)
+ log_debug_errno(r, "Failed to parse T2 time, ignoring: %m");
+ break;
- r = lease_parse_string(option, len, &domainname);
+ case SD_DHCP_OPTION_CLASSLESS_STATIC_ROUTE:
+ r = lease_parse_classless_routes(
+ option, len,
+ &lease->static_route,
+ &lease->static_route_size,
+ &lease->static_route_allocated);
if (r < 0)
- return r;
+ log_debug_errno(r, "Failed to parse classless routes, ignoring: %m");
+ break;
- /* Chop off trailing dot of domain name that some DHCP
- * servers send us back. Internally we want to store
- * host names without trailing dots and
- * host_name_is_valid() doesn't accept them. */
- e = endswith(domainname, ".");
- if (e)
- *e = 0;
+ case SD_DHCP_OPTION_NEW_TZDB_TIMEZONE: {
+ _cleanup_free_ char *tz = NULL;
- if (is_localhost(domainname))
- break;
+ r = lease_parse_string(option, len, &tz);
+ if (r < 0) {
+ log_debug_errno(r, "Failed to parse timezone option, ignoring: %m");
+ return 0;
+ }
- r = dns_name_is_valid(domainname);
- if (r <= 0) {
- if (r < 0)
- log_error_errno(r, "Failed to validate domain name: %s: %m", domainname);
- if (r == 0)
- log_warning("Domain name is not valid, ignoring: %s", domainname);
- break;
+ if (!timezone_is_valid(tz)) {
+ log_debug_errno(r, "Timezone is not valid, ignoring: %m");
+ return 0;
}
- free(lease->domainname);
- lease->domainname = domainname;
- domainname = NULL;
+ free(lease->timezone);
+ lease->timezone = tz;
+ tz = NULL;
break;
}
- case DHCP_OPTION_HOST_NAME:
- {
- _cleanup_free_ char *hostname = NULL;
- char *e;
- r = lease_parse_string(option, len, &hostname);
- if (r < 0)
- return r;
+ case SD_DHCP_OPTION_VENDOR_SPECIFIC:
- e = endswith(hostname, ".");
- if (e)
- *e = 0;
+ if (len <= 0)
+ lease->vendor_specific = mfree(lease->vendor_specific);
+ else {
+ void *p;
- if (!hostname_is_valid(hostname) || is_localhost(hostname))
- break;
+ p = memdup(option, len);
+ if (!p)
+ return -ENOMEM;
- free(lease->hostname);
- lease->hostname = hostname;
- hostname = NULL;
+ free(lease->vendor_specific);
+ lease->vendor_specific = p;
+ }
+ lease->vendor_specific_len = len;
break;
- }
- case DHCP_OPTION_ROOT_PATH:
- r = lease_parse_string(option, len, &lease->root_path);
+
+ case SD_DHCP_OPTION_PRIVATE_BASE ... SD_DHCP_OPTION_PRIVATE_LAST:
+ r = dhcp_lease_insert_private_option(lease, code, option, len);
if (r < 0)
return r;
break;
- case DHCP_OPTION_RENEWAL_T1_TIME:
- lease_parse_u32(option, len, &lease->t1, 1);
-
- break;
-
- case DHCP_OPTION_REBINDING_T2_TIME:
- lease_parse_u32(option, len, &lease->t2, 1);
-
+ default:
+ log_debug("Ignoring option DHCP option %"PRIu8" while parsing.", code);
break;
+ }
- case DHCP_OPTION_ENABLE_IP_FORWARDING:
- lease_parse_bool(option, len, &lease->ip_forward);
-
- break;
-
- case DHCP_OPTION_ENABLE_IP_FORWARDING_NL:
- lease_parse_bool(option, len, &lease->ip_forward_non_local);
-
- break;
+ return 0;
+}
- case DHCP_OPTION_CLASSLESS_STATIC_ROUTE:
- r = lease_parse_classless_routes(option, len, &lease->static_route, &lease->static_route_size,
- &lease->static_route_allocated);
- if (r < 0)
- return r;
+int dhcp_lease_insert_private_option(sd_dhcp_lease *lease, uint8_t tag, const void *data, uint8_t len) {
+ struct sd_dhcp_raw_option *cur, *option;
- break;
+ assert(lease);
- case DHCP_OPTION_VENDOR_SPECIFIC:
- if (len >= 1) {
- free(lease->vendor_specific);
- lease->vendor_specific = memdup(option, len);
- if (!lease->vendor_specific)
- return -ENOMEM;
- lease->vendor_specific_len = len;
+ LIST_FOREACH(options, cur, lease->private_options) {
+ if (tag < cur->tag)
+ break;
+ if (tag == cur->tag) {
+ log_debug("Ignoring duplicate option, tagged %i.", tag);
+ return 0;
}
+ }
+
+ option = new(struct sd_dhcp_raw_option, 1);
+ if (!option)
+ return -ENOMEM;
- break;
+ option->tag = tag;
+ option->length = len;
+ option->data = memdup(data, len);
+ if (!option->data) {
+ free(option);
+ return -ENOMEM;
}
+ LIST_INSERT_BEFORE(options, lease->private_options, cur, option);
return 0;
}
@@ -620,22 +717,24 @@ int dhcp_lease_new(sd_dhcp_lease **ret) {
return -ENOMEM;
lease->router = INADDR_ANY;
- lease->n_ref = REFCNT_INIT;
+ lease->n_ref = 1;
*ret = lease;
return 0;
}
-int sd_dhcp_lease_save(sd_dhcp_lease *lease, const char *lease_file) {
+int dhcp_lease_save(sd_dhcp_lease *lease, const char *lease_file) {
_cleanup_free_ char *temp_path = NULL;
_cleanup_fclose_ FILE *f = NULL;
+ struct sd_dhcp_raw_option *option;
struct in_addr address;
const struct in_addr *addresses;
- const uint8_t *client_id, *data;
+ const void *client_id, *data;
size_t client_id_len, data_len;
const char *string;
uint16_t mtu;
- struct sd_dhcp_route *routes;
+ _cleanup_free_ sd_dhcp_route **routes = NULL;
+ uint32_t t1, t2, lifetime;
int r;
assert(lease);
@@ -643,23 +742,20 @@ int sd_dhcp_lease_save(sd_dhcp_lease *lease, const char *lease_file) {
r = fopen_temporary(lease_file, &f, &temp_path);
if (r < 0)
- goto finish;
+ goto fail;
fchmod(fileno(f), 0644);
- r = sd_dhcp_lease_get_address(lease, &address);
- if (r < 0)
- goto finish;
-
fprintf(f,
- "# This is private data. Do not parse.\n"
- "ADDRESS=%s\n", inet_ntoa(address));
+ "# This is private data. Do not parse.\n");
- r = sd_dhcp_lease_get_netmask(lease, &address);
- if (r < 0)
- goto finish;
+ r = sd_dhcp_lease_get_address(lease, &address);
+ if (r >= 0)
+ fprintf(f, "ADDRESS=%s\n", inet_ntoa(address));
- fprintf(f, "NETMASK=%s\n", inet_ntoa(address));
+ r = sd_dhcp_lease_get_netmask(lease, &address);
+ if (r >= 0)
+ fprintf(f, "NETMASK=%s\n", inet_ntoa(address));
r = sd_dhcp_lease_get_router(lease, &address);
if (r >= 0)
@@ -667,28 +763,45 @@ int sd_dhcp_lease_save(sd_dhcp_lease *lease, const char *lease_file) {
r = sd_dhcp_lease_get_server_identifier(lease, &address);
if (r >= 0)
- fprintf(f, "SERVER_ADDRESS=%s\n",
- inet_ntoa(address));
+ fprintf(f, "SERVER_ADDRESS=%s\n", inet_ntoa(address));
r = sd_dhcp_lease_get_next_server(lease, &address);
if (r >= 0)
fprintf(f, "NEXT_SERVER=%s\n", inet_ntoa(address));
+ r = sd_dhcp_lease_get_broadcast(lease, &address);
+ if (r >= 0)
+ fprintf(f, "BROADCAST=%s\n", inet_ntoa(address));
+
r = sd_dhcp_lease_get_mtu(lease, &mtu);
if (r >= 0)
fprintf(f, "MTU=%" PRIu16 "\n", mtu);
- fputs("DNS=", f);
- r = sd_dhcp_lease_get_dns(lease, &addresses);
+ r = sd_dhcp_lease_get_t1(lease, &t1);
if (r >= 0)
+ fprintf(f, "T1=%" PRIu32 "\n", t1);
+
+ r = sd_dhcp_lease_get_t2(lease, &t2);
+ if (r >= 0)
+ fprintf(f, "T2=%" PRIu32 "\n", t2);
+
+ r = sd_dhcp_lease_get_lifetime(lease, &lifetime);
+ if (r >= 0)
+ fprintf(f, "LIFETIME=%" PRIu32 "\n", lifetime);
+
+ r = sd_dhcp_lease_get_dns(lease, &addresses);
+ if (r > 0) {
+ fputs("DNS=", f);
serialize_in_addrs(f, addresses, r);
- fputs("\n", f);
+ fputs("\n", f);
+ }
- fputs("NTP=", f);
r = sd_dhcp_lease_get_ntp(lease, &addresses);
- if (r >= 0)
+ if (r > 0) {
+ fputs("NTP=", f);
serialize_in_addrs(f, addresses, r);
- fputs("\n", f);
+ fputs("\n", f);
+ }
r = sd_dhcp_lease_get_domainname(lease, &string);
if (r >= 0)
@@ -703,17 +816,21 @@ int sd_dhcp_lease_save(sd_dhcp_lease *lease, const char *lease_file) {
fprintf(f, "ROOT_PATH=%s\n", string);
r = sd_dhcp_lease_get_routes(lease, &routes);
- if (r >= 0)
+ if (r > 0)
serialize_dhcp_routes(f, "ROUTES", routes, r);
+ r = sd_dhcp_lease_get_timezone(lease, &string);
+ if (r >= 0)
+ fprintf(f, "TIMEZONE=%s\n", string);
+
r = sd_dhcp_lease_get_client_id(lease, &client_id, &client_id_len);
if (r >= 0) {
- _cleanup_free_ char *client_id_hex;
+ _cleanup_free_ char *client_id_hex = NULL;
client_id_hex = hexmem(client_id, client_id_len);
if (!client_id_hex) {
r = -ENOMEM;
- goto finish;
+ goto fail;
}
fprintf(f, "CLIENTID=%s\n", client_id_hex);
}
@@ -725,37 +842,60 @@ int sd_dhcp_lease_save(sd_dhcp_lease *lease, const char *lease_file) {
option_hex = hexmem(data, data_len);
if (!option_hex) {
r = -ENOMEM;
- goto finish;
+ goto fail;
}
fprintf(f, "VENDOR_SPECIFIC=%s\n", option_hex);
}
- r = 0;
+ LIST_FOREACH(options, option, lease->private_options) {
+ char key[strlen("OPTION_000")+1];
- fflush(f);
+ xsprintf(key, "OPTION_%" PRIu8, option->tag);
+ r = serialize_dhcp_option(f, key, option->data, option->length);
+ if (r < 0)
+ goto fail;
+ }
- if (ferror(f) || rename(temp_path, lease_file) < 0) {
+ r = fflush_and_check(f);
+ if (r < 0)
+ goto fail;
+
+ if (rename(temp_path, lease_file) < 0) {
r = -errno;
- unlink(lease_file);
- unlink(temp_path);
+ goto fail;
}
-finish:
- if (r < 0)
- log_error_errno(r, "Failed to save lease data %s: %m", lease_file);
+ return 0;
- return r;
+fail:
+ if (temp_path)
+ (void) unlink(temp_path);
+
+ return log_error_errno(r, "Failed to save lease data %s: %m", lease_file);
}
-int sd_dhcp_lease_load(sd_dhcp_lease **ret, const char *lease_file) {
- _cleanup_dhcp_lease_unref_ sd_dhcp_lease *lease = NULL;
- _cleanup_free_ char *address = NULL, *router = NULL, *netmask = NULL,
- *server_address = NULL, *next_server = NULL,
- *dns = NULL, *ntp = NULL, *mtu = NULL,
- *routes = NULL, *client_id_hex = NULL,
- *vendor_specific_hex = NULL;
- struct in_addr addr;
- int r;
+int dhcp_lease_load(sd_dhcp_lease **ret, const char *lease_file) {
+
+ _cleanup_(sd_dhcp_lease_unrefp) sd_dhcp_lease *lease = NULL;
+ _cleanup_free_ char
+ *address = NULL,
+ *router = NULL,
+ *netmask = NULL,
+ *server_address = NULL,
+ *next_server = NULL,
+ *broadcast = NULL,
+ *dns = NULL,
+ *ntp = NULL,
+ *mtu = NULL,
+ *routes = NULL,
+ *client_id_hex = NULL,
+ *vendor_specific_hex = NULL,
+ *lifetime = NULL,
+ *t1 = NULL,
+ *t2 = NULL,
+ *options[SD_DHCP_OPTION_PRIVATE_LAST - SD_DHCP_OPTION_PRIVATE_BASE + 1] = {};
+
+ int r, i;
assert(lease_file);
assert(ret);
@@ -770,6 +910,7 @@ int sd_dhcp_lease_load(sd_dhcp_lease **ret, const char *lease_file) {
"NETMASK", &netmask,
"SERVER_IDENTIFIER", &server_address,
"NEXT_SERVER", &next_server,
+ "BROADCAST", &broadcast,
"DNS", &dns,
"NTP", &ntp,
"MTU", &mtu,
@@ -778,94 +919,162 @@ int sd_dhcp_lease_load(sd_dhcp_lease **ret, const char *lease_file) {
"ROOT_PATH", &lease->root_path,
"ROUTES", &routes,
"CLIENTID", &client_id_hex,
+ "TIMEZONE", &lease->timezone,
"VENDOR_SPECIFIC", &vendor_specific_hex,
+ "LIFETIME", &lifetime,
+ "T1", &t1,
+ "T2", &t2,
+ "OPTION_224", &options[0],
+ "OPTION_225", &options[1],
+ "OPTION_226", &options[2],
+ "OPTION_227", &options[3],
+ "OPTION_228", &options[4],
+ "OPTION_229", &options[5],
+ "OPTION_230", &options[6],
+ "OPTION_231", &options[7],
+ "OPTION_232", &options[8],
+ "OPTION_233", &options[9],
+ "OPTION_234", &options[10],
+ "OPTION_235", &options[11],
+ "OPTION_236", &options[12],
+ "OPTION_237", &options[13],
+ "OPTION_238", &options[14],
+ "OPTION_239", &options[15],
+ "OPTION_240", &options[16],
+ "OPTION_241", &options[17],
+ "OPTION_242", &options[18],
+ "OPTION_243", &options[19],
+ "OPTION_244", &options[20],
+ "OPTION_245", &options[21],
+ "OPTION_246", &options[22],
+ "OPTION_247", &options[23],
+ "OPTION_248", &options[24],
+ "OPTION_249", &options[25],
+ "OPTION_250", &options[26],
+ "OPTION_251", &options[27],
+ "OPTION_252", &options[28],
+ "OPTION_253", &options[29],
+ "OPTION_254", &options[30],
NULL);
- if (r < 0) {
- if (r == -ENOENT)
- return 0;
-
- return log_error_errno(r, "Failed to read %s: %m", lease_file);
- }
-
- r = inet_pton(AF_INET, address, &addr);
if (r < 0)
return r;
- lease->address = addr.s_addr;
+ if (address) {
+ r = inet_pton(AF_INET, address, &lease->address);
+ if (r <= 0)
+ log_debug("Failed to parse address %s, ignoring.", address);
+ }
if (router) {
- r = inet_pton(AF_INET, router, &addr);
- if (r < 0)
- return r;
-
- lease->router = addr.s_addr;
+ r = inet_pton(AF_INET, router, &lease->router);
+ if (r <= 0)
+ log_debug("Failed to parse router %s, ignoring.", router);
}
- r = inet_pton(AF_INET, netmask, &addr);
- if (r < 0)
- return r;
-
- lease->subnet_mask = addr.s_addr;
+ if (netmask) {
+ r = inet_pton(AF_INET, netmask, &lease->subnet_mask);
+ if (r <= 0)
+ log_debug("Failed to parse netmask %s, ignoring.", netmask);
+ else
+ lease->have_subnet_mask = true;
+ }
if (server_address) {
- r = inet_pton(AF_INET, server_address, &addr);
- if (r < 0)
- return r;
-
- lease->server_address = addr.s_addr;
+ r = inet_pton(AF_INET, server_address, &lease->server_address);
+ if (r <= 0)
+ log_debug("Failed to parse server address %s, ignoring.", server_address);
}
if (next_server) {
- r = inet_pton(AF_INET, next_server, &addr);
- if (r < 0)
- return r;
+ r = inet_pton(AF_INET, next_server, &lease->next_server);
+ if (r <= 0)
+ log_debug("Failed to parse next server %s, ignoring.", next_server);
+ }
- lease->next_server = addr.s_addr;
+ if (broadcast) {
+ r = inet_pton(AF_INET, broadcast, &lease->broadcast);
+ if (r <= 0)
+ log_debug("Failed to parse broadcast address %s, ignoring.", broadcast);
+ else
+ lease->have_broadcast = true;
}
if (dns) {
r = deserialize_in_addrs(&lease->dns, dns);
if (r < 0)
- return r;
-
- lease->dns_size = r;
+ log_debug_errno(r, "Failed to deserialize DNS servers %s, ignoring: %m", dns);
+ else
+ lease->dns_size = r;
}
if (ntp) {
r = deserialize_in_addrs(&lease->ntp, ntp);
if (r < 0)
- return r;
-
- lease->ntp_size = r;
+ log_debug_errno(r, "Failed to deserialize NTP servers %s, ignoring: %m", ntp);
+ else
+ lease->ntp_size = r;
}
if (mtu) {
- uint16_t u;
- if (sscanf(mtu, "%" SCNu16, &u) > 0)
- lease->mtu = u;
+ r = safe_atou16(mtu, &lease->mtu);
+ if (r < 0)
+ log_debug_errno(r, "Failed to parse MTU %s, ignoring: %m", mtu);
}
if (routes) {
- r = deserialize_dhcp_routes(&lease->static_route, &lease->static_route_size,
- &lease->static_route_allocated, routes);
+ r = deserialize_dhcp_routes(
+ &lease->static_route,
+ &lease->static_route_size,
+ &lease->static_route_allocated,
+ routes);
if (r < 0)
- return r;
+ log_debug_errno(r, "Failed to parse DHCP routes %s, ignoring: %m", routes);
}
- if (client_id_hex) {
- if (strlen(client_id_hex) % 2)
- return -EINVAL;
+ if (lifetime) {
+ r = safe_atou32(lifetime, &lease->lifetime);
+ if (r < 0)
+ log_debug_errno(r, "Failed to parse lifetime %s, ignoring: %m", lifetime);
+ }
- r = unhexmem(client_id_hex, strlen(client_id_hex), (void**) &lease->client_id, &lease->client_id_len);
+ if (t1) {
+ r = safe_atou32(t1, &lease->t1);
if (r < 0)
- return r;
+ log_debug_errno(r, "Failed to parse T1 %s, ignoring: %m", t1);
+ }
+
+ if (t2) {
+ r = safe_atou32(t2, &lease->t2);
+ if (r < 0)
+ log_debug_errno(r, "Failed to parse T2 %s, ignoring: %m", t2);
+ }
+
+ if (client_id_hex) {
+ r = deserialize_dhcp_option(&lease->client_id, &lease->client_id_len, client_id_hex);
+ if (r < 0)
+ log_debug_errno(r, "Failed to parse client ID %s, ignoring: %m", client_id_hex);
}
if (vendor_specific_hex) {
- if (strlen(vendor_specific_hex) % 2)
- return -EINVAL;
+ r = deserialize_dhcp_option(&lease->vendor_specific, &lease->vendor_specific_len, vendor_specific_hex);
+ if (r < 0)
+ log_debug_errno(r, "Failed to parse vendor specific data %s, ignoring: %m", vendor_specific_hex);
+ }
+
+ for (i = 0; i <= SD_DHCP_OPTION_PRIVATE_LAST - SD_DHCP_OPTION_PRIVATE_BASE; i++) {
+ _cleanup_free_ void *data = NULL;
+ size_t len;
+
+ if (!options[i])
+ continue;
+
+ r = deserialize_dhcp_option(&data, &len, options[i]);
+ if (r < 0) {
+ log_debug_errno(r, "Failed to parse private DHCP option %s, ignoring: %m", options[i]);
+ continue;
+ }
- r = unhexmem(vendor_specific_hex, strlen(vendor_specific_hex), (void**) &lease->vendor_specific, &lease->vendor_specific_len);
+ r = dhcp_lease_insert_private_option(lease, SD_DHCP_OPTION_PRIVATE_BASE + i, data, len);
if (r < 0)
return r;
}
@@ -877,12 +1086,14 @@ int sd_dhcp_lease_load(sd_dhcp_lease **ret, const char *lease_file) {
}
int dhcp_lease_set_default_subnet_mask(sd_dhcp_lease *lease) {
- struct in_addr address;
- struct in_addr mask;
+ struct in_addr address, mask;
int r;
assert(lease);
+ if (lease->address == 0)
+ return -ENODATA;
+
address.s_addr = lease->address;
/* fall back to the default subnet masks based on address class */
@@ -891,35 +1102,77 @@ int dhcp_lease_set_default_subnet_mask(sd_dhcp_lease *lease) {
return r;
lease->subnet_mask = mask.s_addr;
+ lease->have_subnet_mask = true;
return 0;
}
-int sd_dhcp_lease_get_client_id(sd_dhcp_lease *lease, const uint8_t **client_id,
- size_t *client_id_len) {
+int sd_dhcp_lease_get_client_id(sd_dhcp_lease *lease, const void **client_id, size_t *client_id_len) {
assert_return(lease, -EINVAL);
assert_return(client_id, -EINVAL);
assert_return(client_id_len, -EINVAL);
+ if (!lease->client_id)
+ return -ENODATA;
+
*client_id = lease->client_id;
*client_id_len = lease->client_id_len;
+
return 0;
}
-int dhcp_lease_set_client_id(sd_dhcp_lease *lease, const uint8_t *client_id,
- size_t client_id_len) {
+int dhcp_lease_set_client_id(sd_dhcp_lease *lease, const void *client_id, size_t client_id_len) {
assert_return(lease, -EINVAL);
- assert_return((!client_id && !client_id_len) ||
- (client_id && client_id_len), -EINVAL);
+ assert_return(client_id || client_id_len <= 0, -EINVAL);
+
+ if (client_id_len <= 0)
+ lease->client_id = mfree(lease->client_id);
+ else {
+ void *p;
- free (lease->client_id);
- lease->client_id = NULL;
- lease->client_id_len = 0;
+ p = memdup(client_id, client_id_len);
+ if (!p)
+ return -ENOMEM;
- if (client_id) {
- lease->client_id = memdup (client_id, client_id_len);
+ free(lease->client_id);
+ lease->client_id = p;
lease->client_id_len = client_id_len;
}
return 0;
}
+
+int sd_dhcp_lease_get_timezone(sd_dhcp_lease *lease, const char **tz) {
+ assert_return(lease, -EINVAL);
+ assert_return(tz, -EINVAL);
+
+ if (!lease->timezone)
+ return -ENODATA;
+
+ *tz = lease->timezone;
+ return 0;
+}
+
+int sd_dhcp_route_get_destination(sd_dhcp_route *route, struct in_addr *destination) {
+ assert_return(route, -EINVAL);
+ assert_return(destination, -EINVAL);
+
+ *destination = route->dst_addr;
+ return 0;
+}
+
+int sd_dhcp_route_get_destination_prefix_length(sd_dhcp_route *route, uint8_t *length) {
+ assert_return(route, -EINVAL);
+ assert_return(length, -EINVAL);
+
+ *length = route->dst_prefixlen;
+ return 0;
+}
+
+int sd_dhcp_route_get_gateway(sd_dhcp_route *route, struct in_addr *gateway) {
+ assert_return(route, -EINVAL);
+ assert_return(gateway, -EINVAL);
+
+ *gateway = route->gw_addr;
+ return 0;
+}
diff --git a/src/libsystemd-network/sd-dhcp-server.c b/src/libsystemd-network/sd-dhcp-server.c
index cc5e032344..11ee2e252e 100644
--- a/src/libsystemd-network/sd-dhcp-server.c
+++ b/src/libsystemd-network/sd-dhcp-server.c
@@ -1,5 +1,3 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
/***
This file is part of systemd.
@@ -22,75 +20,107 @@
#include <sys/ioctl.h>
-#include "siphash24.h"
-
#include "sd-dhcp-server.h"
-#include "dhcp-server-internal.h"
+
+#include "alloc-util.h"
#include "dhcp-internal.h"
+#include "dhcp-server-internal.h"
+#include "fd-util.h"
+#include "in-addr-util.h"
+#include "siphash24.h"
+#include "string-util.h"
+#include "unaligned.h"
-#define DHCP_DEFAULT_LEASE_TIME 3600 /* one hour */
+#define DHCP_DEFAULT_LEASE_TIME_USEC USEC_PER_HOUR
+#define DHCP_MAX_LEASE_TIME_USEC (USEC_PER_HOUR*12)
+
+/* configures the server's address and subnet, and optionally the pool's size and offset into the subnet
+ * the whole pool must fit into the subnet, and may not contain the first (any) nor last (broadcast) address
+ * moreover, the server's own address may be in the pool, and is in that case reserved in order not to
+ * accidentally hand it out */
+int sd_dhcp_server_configure_pool(sd_dhcp_server *server, struct in_addr *address, unsigned char prefixlen, uint32_t offset, uint32_t size) {
+ struct in_addr netmask_addr;
+ be32_t netmask;
+ uint32_t server_off, broadcast_off, size_max;
-int sd_dhcp_server_set_lease_pool(sd_dhcp_server *server,
- struct in_addr *address,
- size_t size) {
assert_return(server, -EINVAL);
assert_return(address, -EINVAL);
- assert_return(address->s_addr, -EINVAL);
- assert_return(size, -EINVAL);
- assert_return(server->pool_start == htobe32(INADDR_ANY), -EBUSY);
- assert_return(!server->pool_size, -EBUSY);
- assert_return(!server->bound_leases, -EBUSY);
+ assert_return(address->s_addr != INADDR_ANY, -EINVAL);
+ assert_return(prefixlen <= 32, -ERANGE);
+ assert_return(server->address == INADDR_ANY, -EBUSY);
+
+ assert_se(in_addr_prefixlen_to_netmask(&netmask_addr, prefixlen));
+ netmask = netmask_addr.s_addr;
+
+ server_off = be32toh(address->s_addr & ~netmask);
+ broadcast_off = be32toh(~netmask);
+
+ /* the server address cannot be the subnet address */
+ assert_return(server_off != 0, -ERANGE);
+
+ /* nor the broadcast address */
+ assert_return(server_off != broadcast_off, -ERANGE);
+
+ /* 0 offset means we should set a default, we skip the first (subnet) address
+ and take the next one */
+ if (offset == 0)
+ offset = 1;
+
+ size_max = (broadcast_off + 1) /* the number of addresses in the subnet */
+ - offset /* exclude the addresses before the offset */
+ - 1; /* exclude the last (broadcast) address */
+
+ /* The pool must contain at least one address */
+ assert_return(size_max >= 1, -ERANGE);
+
+ if (size != 0)
+ assert_return(size <= size_max, -ERANGE);
+ else
+ size = size_max;
server->bound_leases = new0(DHCPLease*, size);
if (!server->bound_leases)
return -ENOMEM;
- server->pool_start = address->s_addr;
+ server->pool_offset = offset;
server->pool_size = size;
- return 0;
-}
-
-int sd_dhcp_server_set_address(sd_dhcp_server *server, struct in_addr *address,
- unsigned char prefixlen) {
- assert_return(server, -EINVAL);
- assert_return(address, -EINVAL);
- assert_return(address->s_addr, -EINVAL);
- assert_return(prefixlen <= 32, -ERANGE);
- assert_return(server->address == htobe32(INADDR_ANY), -EBUSY);
- assert_return(server->netmask == htobe32(INADDR_ANY), -EBUSY);
-
server->address = address->s_addr;
- server->netmask = htobe32(0xfffffffflu << (32 - prefixlen));
+ server->netmask = netmask;
+ server->subnet = address->s_addr & netmask;
+
+ if (server_off >= offset && server_off - offset < size)
+ server->bound_leases[server_off - offset] = &server->invalid_lease;
return 0;
}
-bool sd_dhcp_server_is_running(sd_dhcp_server *server) {
- assert_return(server, -EINVAL);
+int sd_dhcp_server_is_running(sd_dhcp_server *server) {
+ assert_return(server, false);
return !!server->receive_message;
}
sd_dhcp_server *sd_dhcp_server_ref(sd_dhcp_server *server) {
- if (server)
- assert_se(REFCNT_INC(server->n_ref) >= 2);
+
+ if (!server)
+ return NULL;
+
+ assert(server->n_ref >= 1);
+ server->n_ref++;
return server;
}
-unsigned long client_id_hash_func(const void *p,
- const uint8_t hash_key[HASH_KEY_SIZE]) {
- uint64_t u;
+void client_id_hash_func(const void *p, struct siphash *state) {
const DHCPClientId *id = p;
assert(id);
assert(id->length);
assert(id->data);
- siphash24((uint8_t*) &u, id->data, id->length, hash_key);
-
- return (unsigned long) u;
+ siphash24_compress(&id->length, sizeof(id->length), state);
+ siphash24_compress(id->data, id->length, state);
}
int client_id_compare_func(const void *_a, const void *_b) {
@@ -127,7 +157,10 @@ sd_dhcp_server *sd_dhcp_server_unref(sd_dhcp_server *server) {
if (!server)
return NULL;
- if (REFCNT_DEC(server->n_ref) > 0)
+ assert(server->n_ref >= 1);
+ server->n_ref--;
+
+ if (server->n_ref > 0)
return NULL;
log_dhcp_server(server, "UNREF");
@@ -136,6 +169,10 @@ sd_dhcp_server *sd_dhcp_server_unref(sd_dhcp_server *server) {
sd_event_unref(server->event);
+ free(server->timezone);
+ free(server->dns);
+ free(server->ntp);
+
while ((lease = hashmap_steal_first(server->leases_by_client_id)))
dhcp_lease_free(lease);
hashmap_free(server->leases_by_client_id);
@@ -147,7 +184,7 @@ sd_dhcp_server *sd_dhcp_server_unref(sd_dhcp_server *server) {
}
int sd_dhcp_server_new(sd_dhcp_server **ret, int ifindex) {
- _cleanup_dhcp_server_unref_ sd_dhcp_server *server = NULL;
+ _cleanup_(sd_dhcp_server_unrefp) sd_dhcp_server *server = NULL;
assert_return(ret, -EINVAL);
assert_return(ifindex > 0, -EINVAL);
@@ -156,13 +193,15 @@ int sd_dhcp_server_new(sd_dhcp_server **ret, int ifindex) {
if (!server)
return -ENOMEM;
- server->n_ref = REFCNT_INIT;
+ server->n_ref = 1;
server->fd_raw = -1;
server->fd = -1;
server->address = htobe32(INADDR_ANY);
server->netmask = htobe32(INADDR_ANY);
- server->index = ifindex;
+ server->ifindex = ifindex;
server->leases_by_client_id = hashmap_new(&client_id_hash_ops);
+ server->default_lease_time = DIV_ROUND_UP(DHCP_DEFAULT_LEASE_TIME_USEC, USEC_PER_SEC);
+ server->max_lease_time = DIV_ROUND_UP(DHCP_MAX_LEASE_TIME_USEC, USEC_PER_SEC);
*ret = server;
server = NULL;
@@ -170,8 +209,7 @@ int sd_dhcp_server_new(sd_dhcp_server **ret, int ifindex) {
return 0;
}
-int sd_dhcp_server_attach_event(sd_dhcp_server *server, sd_event *event,
- int priority) {
+int sd_dhcp_server_attach_event(sd_dhcp_server *server, sd_event *event, int64_t priority) {
int r;
assert_return(server, -EINVAL);
@@ -222,14 +260,13 @@ static int dhcp_server_send_unicast_raw(sd_dhcp_server *server,
DHCPPacket *packet, size_t len) {
union sockaddr_union link = {
.ll.sll_family = AF_PACKET,
- .ll.sll_protocol = htons(ETH_P_IP),
- .ll.sll_ifindex = server->index,
+ .ll.sll_protocol = htobe16(ETH_P_IP),
+ .ll.sll_ifindex = server->ifindex,
.ll.sll_halen = ETH_ALEN,
};
- int r;
assert(server);
- assert(server->index > 0);
+ assert(server->ifindex > 0);
assert(server->address);
assert(packet);
assert(len > sizeof(DHCPPacket));
@@ -240,18 +277,15 @@ static int dhcp_server_send_unicast_raw(sd_dhcp_server *server,
packet->dhcp.yiaddr,
DHCP_PORT_CLIENT, len);
- r = dhcp_network_send_raw_socket(server->fd_raw, &link, packet, len);
- if (r < 0)
- return r;
-
- return 0;
+ return dhcp_network_send_raw_socket(server->fd_raw, &link, packet, len);
}
static int dhcp_server_send_udp(sd_dhcp_server *server, be32_t destination,
+ uint16_t destination_port,
DHCPMessage *message, size_t len) {
union sockaddr_union dest = {
.in.sin_family = AF_INET,
- .in.sin_port = htobe16(DHCP_PORT_CLIENT),
+ .in.sin_port = htobe16(destination_port),
.in.sin_addr.s_addr = destination,
};
struct iovec iov = {
@@ -290,7 +324,7 @@ static int dhcp_server_send_udp(sd_dhcp_server *server, be32_t destination,
pktinfo = (struct in_pktinfo*) CMSG_DATA(cmsg);
assert(pktinfo);
- pktinfo->ipi_ifindex = server->index;
+ pktinfo->ipi_ifindex = server->ifindex;
pktinfo->ipi_spec_dst.s_addr = server->address;
r = sendmsg(server->fd, &msg, 0);
@@ -310,6 +344,7 @@ int dhcp_server_send_packet(sd_dhcp_server *server,
DHCPRequest *req, DHCPPacket *packet,
int type, size_t optoffset) {
be32_t destination = INADDR_ANY;
+ uint16_t destination_port = DHCP_PORT_CLIENT;
int r;
assert(server);
@@ -319,13 +354,13 @@ int dhcp_server_send_packet(sd_dhcp_server *server,
assert(packet);
r = dhcp_option_append(&packet->dhcp, req->max_optlen, &optoffset, 0,
- DHCP_OPTION_SERVER_IDENTIFIER,
+ SD_DHCP_OPTION_SERVER_IDENTIFIER,
4, &server->address);
if (r < 0)
return r;
r = dhcp_option_append(&packet->dhcp, req->max_optlen, &optoffset, 0,
- DHCP_OPTION_END, 0, NULL);
+ SD_DHCP_OPTION_END, 0, NULL);
if (r < 0)
return r;
@@ -354,17 +389,19 @@ int dhcp_server_send_packet(sd_dhcp_server *server,
*/
if (req->message->giaddr) {
destination = req->message->giaddr;
+ destination_port = DHCP_PORT_SERVER;
if (type == DHCP_NAK)
packet->dhcp.flags = htobe16(0x8000);
} else if (req->message->ciaddr && type != DHCP_NAK)
destination = req->message->ciaddr;
if (destination != INADDR_ANY)
- return dhcp_server_send_udp(server, destination, &packet->dhcp,
+ return dhcp_server_send_udp(server, destination,
+ destination_port, &packet->dhcp,
sizeof(DHCPMessage) + optoffset);
else if (requested_broadcast(req) || type == DHCP_NAK)
return dhcp_server_send_udp(server, INADDR_BROADCAST,
- &packet->dhcp,
+ destination_port, &packet->dhcp,
sizeof(DHCPMessage) + optoffset);
else
/* we cannot send UDP packet to specific MAC address when the
@@ -422,20 +459,22 @@ static int server_send_offer(sd_dhcp_server *server, DHCPRequest *req,
lease_time = htobe32(req->lifetime);
r = dhcp_option_append(&packet->dhcp, req->max_optlen, &offset, 0,
- DHCP_OPTION_IP_ADDRESS_LEASE_TIME, 4,
+ SD_DHCP_OPTION_IP_ADDRESS_LEASE_TIME, 4,
&lease_time);
if (r < 0)
return r;
r = dhcp_option_append(&packet->dhcp, req->max_optlen, &offset, 0,
- DHCP_OPTION_SUBNET_MASK, 4, &server->netmask);
+ SD_DHCP_OPTION_SUBNET_MASK, 4, &server->netmask);
if (r < 0)
return r;
- r = dhcp_option_append(&packet->dhcp, req->max_optlen, &offset, 0,
- DHCP_OPTION_ROUTER, 4, &server->address);
- if (r < 0)
- return r;
+ if (server->emit_router) {
+ r = dhcp_option_append(&packet->dhcp, req->max_optlen, &offset, 0,
+ SD_DHCP_OPTION_ROUTER, 4, &server->address);
+ if (r < 0)
+ return r;
+ }
r = dhcp_server_send_packet(server, req, packet, DHCP_OFFER, offset);
if (r < 0)
@@ -459,20 +498,49 @@ static int server_send_ack(sd_dhcp_server *server, DHCPRequest *req,
lease_time = htobe32(req->lifetime);
r = dhcp_option_append(&packet->dhcp, req->max_optlen, &offset, 0,
- DHCP_OPTION_IP_ADDRESS_LEASE_TIME, 4,
+ SD_DHCP_OPTION_IP_ADDRESS_LEASE_TIME, 4,
&lease_time);
if (r < 0)
return r;
r = dhcp_option_append(&packet->dhcp, req->max_optlen, &offset, 0,
- DHCP_OPTION_SUBNET_MASK, 4, &server->netmask);
+ SD_DHCP_OPTION_SUBNET_MASK, 4, &server->netmask);
if (r < 0)
return r;
- r = dhcp_option_append(&packet->dhcp, req->max_optlen, &offset, 0,
- DHCP_OPTION_ROUTER, 4, &server->address);
- if (r < 0)
- return r;
+ if (server->emit_router) {
+ r = dhcp_option_append(&packet->dhcp, req->max_optlen, &offset, 0,
+ SD_DHCP_OPTION_ROUTER, 4, &server->address);
+ if (r < 0)
+ return r;
+ }
+
+ if (server->n_dns > 0) {
+ r = dhcp_option_append(
+ &packet->dhcp, req->max_optlen, &offset, 0,
+ SD_DHCP_OPTION_DOMAIN_NAME_SERVER,
+ sizeof(struct in_addr) * server->n_dns, server->dns);
+ if (r < 0)
+ return r;
+ }
+
+ if (server->n_ntp > 0) {
+ r = dhcp_option_append(
+ &packet->dhcp, req->max_optlen, &offset, 0,
+ SD_DHCP_OPTION_NTP_SERVER,
+ sizeof(struct in_addr) * server->n_ntp, server->ntp);
+ if (r < 0)
+ return r;
+ }
+
+ if (server->timezone) {
+ r = dhcp_option_append(
+ &packet->dhcp, req->max_optlen, &offset, 0,
+ SD_DHCP_OPTION_NEW_TZDB_TIMEZONE,
+ strlen(server->timezone), server->timezone);
+ if (r < 0)
+ return r;
+ }
r = dhcp_server_send_packet(server, req, packet, DHCP_ACK, offset);
if (r < 0)
@@ -490,11 +558,7 @@ static int server_send_nak(sd_dhcp_server *server, DHCPRequest *req) {
if (r < 0)
return r;
- r = dhcp_server_send_packet(server, req, packet, DHCP_NAK, offset);
- if (r < 0)
- return r;
-
- return 0;
+ return dhcp_server_send_packet(server, req, packet, DHCP_NAK, offset);
}
static int server_send_forcerenew(sd_dhcp_server *server, be32_t address,
@@ -518,13 +582,14 @@ static int server_send_forcerenew(sd_dhcp_server *server, be32_t address,
return r;
r = dhcp_option_append(&packet->dhcp, DHCP_MIN_OPTIONS_SIZE,
- &optoffset, 0, DHCP_OPTION_END, 0, NULL);
+ &optoffset, 0, SD_DHCP_OPTION_END, 0, NULL);
if (r < 0)
return r;
memcpy(&packet->dhcp.chaddr, chaddr, ETH_ALEN);
- r = dhcp_server_send_udp(server, address, &packet->dhcp,
+ r = dhcp_server_send_udp(server, address, DHCP_PORT_CLIENT,
+ &packet->dhcp,
sizeof(DHCPMessage) + optoffset);
if (r < 0)
return r;
@@ -532,29 +597,28 @@ static int server_send_forcerenew(sd_dhcp_server *server, be32_t address,
return 0;
}
-static int parse_request(uint8_t code, uint8_t len, const uint8_t *option,
- void *user_data) {
- DHCPRequest *req = user_data;
+static int parse_request(uint8_t code, uint8_t len, const void *option, void *userdata) {
+ DHCPRequest *req = userdata;
assert(req);
switch(code) {
- case DHCP_OPTION_IP_ADDRESS_LEASE_TIME:
+ case SD_DHCP_OPTION_IP_ADDRESS_LEASE_TIME:
if (len == 4)
- req->lifetime = be32toh(*(be32_t*)option);
+ req->lifetime = unaligned_read_be32(option);
break;
- case DHCP_OPTION_REQUESTED_IP_ADDRESS:
+ case SD_DHCP_OPTION_REQUESTED_IP_ADDRESS:
if (len == 4)
- req->requested_ip = *(be32_t*)option;
+ memcpy(&req->requested_ip, option, sizeof(be32_t));
break;
- case DHCP_OPTION_SERVER_IDENTIFIER:
+ case SD_DHCP_OPTION_SERVER_IDENTIFIER:
if (len == 4)
- req->server_id = *(be32_t*)option;
+ memcpy(&req->server_id, option, sizeof(be32_t));
break;
- case DHCP_OPTION_CLIENT_IDENTIFIER:
+ case SD_DHCP_OPTION_CLIENT_IDENTIFIER:
if (len >= 2) {
uint8_t *data;
@@ -568,10 +632,10 @@ static int parse_request(uint8_t code, uint8_t len, const uint8_t *option,
}
break;
- case DHCP_OPTION_MAXIMUM_MESSAGE_SIZE:
- if (len == 2)
- req->max_optlen = be16toh(*(be16_t*)option) -
- - sizeof(DHCPPacket);
+ case SD_DHCP_OPTION_MAXIMUM_MESSAGE_SIZE:
+
+ if (len == 2 && unaligned_read_be16(option) >= sizeof(DHCPPacket))
+ req->max_optlen = unaligned_read_be16(option) - sizeof(DHCPPacket);
break;
}
@@ -590,7 +654,7 @@ static void dhcp_request_free(DHCPRequest *req) {
DEFINE_TRIVIAL_CLEANUP_FUNC(DHCPRequest*, dhcp_request_free);
#define _cleanup_dhcp_request_free_ _cleanup_(dhcp_request_freep)
-static int ensure_sane_request(DHCPRequest *req, DHCPMessage *message) {
+static int ensure_sane_request(sd_dhcp_server *server, DHCPRequest *req, DHCPMessage *message) {
assert(req);
assert(message);
@@ -599,23 +663,27 @@ static int ensure_sane_request(DHCPRequest *req, DHCPMessage *message) {
/* set client id based on MAC address if client did not send an explicit
one */
if (!req->client_id.data) {
- uint8_t *data;
+ void *data;
- data = new0(uint8_t, ETH_ALEN + 1);
+ data = malloc0(ETH_ALEN + 1);
if (!data)
return -ENOMEM;
+ ((uint8_t*) data)[0] = 0x01;
+ memcpy((uint8_t*) data + 1, &message->chaddr, ETH_ALEN);
+
req->client_id.length = ETH_ALEN + 1;
req->client_id.data = data;
- req->client_id.data[0] = 0x01;
- memcpy(&req->client_id.data[1], &message->chaddr, ETH_ALEN);
}
if (req->max_optlen < DHCP_MIN_OPTIONS_SIZE)
req->max_optlen = DHCP_MIN_OPTIONS_SIZE;
- if (!req->lifetime)
- req->lifetime = DHCP_DEFAULT_LEASE_TIME;
+ if (req->lifetime <= 0)
+ req->lifetime = MAX(1ULL, server->default_lease_time);
+
+ if (server->max_lease_time > 0 && req->lifetime > server->max_lease_time)
+ req->lifetime = server->max_lease_time;
return 0;
}
@@ -626,17 +694,19 @@ static int get_pool_offset(sd_dhcp_server *server, be32_t requested_ip) {
if (!server->pool_size)
return -EINVAL;
- if (be32toh(requested_ip) < be32toh(server->pool_start) ||
- be32toh(requested_ip) >= be32toh(server->pool_start) +
- + server->pool_size)
- return -EINVAL;
+ if (be32toh(requested_ip) < (be32toh(server->subnet) | server->pool_offset) ||
+ be32toh(requested_ip) >= (be32toh(server->subnet) | (server->pool_offset + server->pool_size)))
+ return -ERANGE;
- return be32toh(requested_ip) - be32toh(server->pool_start);
+ return be32toh(requested_ip & ~server->netmask) - server->pool_offset;
}
+#define HASH_KEY SD_ID128_MAKE(0d,1d,fe,bd,f1,24,bd,b3,47,f1,dd,6e,73,21,93,30)
+
int dhcp_server_handle_message(sd_dhcp_server *server, DHCPMessage *message,
size_t length) {
_cleanup_dhcp_request_free_ DHCPRequest *req = NULL;
+ _cleanup_free_ char *error_message = NULL;
DHCPLease *existing_lease;
int type, r;
@@ -652,11 +722,11 @@ int dhcp_server_handle_message(sd_dhcp_server *server, DHCPMessage *message,
if (!req)
return -ENOMEM;
- type = dhcp_option_parse(message, length, parse_request, req);
+ type = dhcp_option_parse(message, length, parse_request, req, &error_message);
if (type < 0)
return 0;
- r = ensure_sane_request(req, message);
+ r = ensure_sane_request(server, req, message);
if (r < 0)
/* this only fails on critical errors */
return r;
@@ -665,8 +735,8 @@ int dhcp_server_handle_message(sd_dhcp_server *server, DHCPMessage *message,
&req->client_id);
switch(type) {
- case DHCP_DISCOVER:
- {
+
+ case DHCP_DISCOVER: {
be32_t address = INADDR_ANY;
unsigned i;
@@ -681,12 +751,25 @@ int dhcp_server_handle_message(sd_dhcp_server *server, DHCPMessage *message,
if (existing_lease)
address = existing_lease->address;
else {
+ struct siphash state;
+ uint64_t hash;
+ uint32_t next_offer;
+
+ /* even with no persistence of leases, we try to offer the same client
+ the same IP address. we do this by using the hash of the client id
+ as the offset into the pool of leases when finding the next free one */
+
+ siphash24_init(&state, HASH_KEY.bytes);
+ client_id_hash_func(&req->client_id, &state);
+ hash = htole64(siphash24_finalize(&state));
+ next_offer = hash % server->pool_size;
+
for (i = 0; i < server->pool_size; i++) {
- if (!server->bound_leases[server->next_offer]) {
- address = htobe32(be32toh(server->pool_start) + server->next_offer);
+ if (!server->bound_leases[next_offer]) {
+ address = server->subnet | htobe32(server->pool_offset + next_offer);
break;
} else
- server->next_offer = (server->next_offer + 1) % server->pool_size;
+ next_offer = (next_offer + 1) % server->pool_size;
}
}
@@ -709,16 +792,13 @@ int dhcp_server_handle_message(sd_dhcp_server *server, DHCPMessage *message,
break;
}
case DHCP_DECLINE:
- log_dhcp_server(server, "DECLINE (0x%x)",
- be32toh(req->message->xid));
+ log_dhcp_server(server, "DECLINE (0x%x): %s", be32toh(req->message->xid), strna(error_message));
/* TODO: make sure we don't offer this address again */
return 1;
- break;
- case DHCP_REQUEST:
- {
+ case DHCP_REQUEST: {
be32_t address;
bool init_reboot = false;
int pool_offset;
@@ -796,8 +876,12 @@ int dhcp_server_handle_message(sd_dhcp_server *server, DHCPMessage *message,
r = sd_event_now(server->event,
clock_boottime_or_monotonic(),
&time_now);
- if (r < 0)
- time_now = now(clock_boottime_or_monotonic());
+ if (r < 0) {
+ if (!existing_lease)
+ dhcp_lease_free(lease);
+ return r;
+ }
+
lease->expiration = req->lifetime * USEC_PER_SEC + time_now;
r = server_send_ack(server, req, address);
@@ -836,6 +920,7 @@ int dhcp_server_handle_message(sd_dhcp_server *server, DHCPMessage *message,
break;
}
+
case DHCP_RELEASE: {
int pool_offset;
@@ -879,17 +964,15 @@ static int server_receive_message(sd_event_source *s, int fd,
.msg_controllen = sizeof(cmsgbuf),
};
struct cmsghdr *cmsg;
- int buflen = 0, len, r;
+ ssize_t buflen, len;
assert(server);
- r = ioctl(fd, FIONREAD, &buflen);
- if (r < 0)
- return r;
+ buflen = next_datagram_size_fd(fd);
if (buflen < 0)
- return -EIO;
+ return buflen;
- message = malloc0(buflen);
+ message = malloc(buflen);
if (!message)
return -ENOMEM;
@@ -897,9 +980,12 @@ static int server_receive_message(sd_event_source *s, int fd,
iov.iov_len = buflen;
len = recvmsg(fd, &msg, 0);
- if (len < buflen)
- return 0;
- else if ((size_t)len < sizeof(DHCPMessage))
+ if (len < 0) {
+ if (errno == EAGAIN || errno == EINTR)
+ return 0;
+
+ return -errno;
+ } else if ((size_t)len < sizeof(DHCPMessage))
return 0;
CMSG_FOREACH(cmsg, &msg) {
@@ -910,7 +996,7 @@ static int server_receive_message(sd_event_source *s, int fd,
/* TODO figure out if this can be done as a filter on
* the socket, like for IPv6 */
- if (server->index != info->ipi_ifindex)
+ if (server->ifindex != info->ipi_ifindex)
return 0;
break;
@@ -975,7 +1061,7 @@ int sd_dhcp_server_forcerenew(sd_dhcp_server *server) {
for (i = 0; i < server->pool_size; i++) {
DHCPLease *lease = server->bound_leases[i];
- if (!lease)
+ if (!lease || lease == &server->invalid_lease)
continue;
r = server_send_forcerenew(server, lease->address,
@@ -989,3 +1075,102 @@ int sd_dhcp_server_forcerenew(sd_dhcp_server *server) {
return r;
}
+
+int sd_dhcp_server_set_timezone(sd_dhcp_server *server, const char *tz) {
+ int r;
+
+ assert_return(server, -EINVAL);
+ assert_return(timezone_is_valid(tz), -EINVAL);
+
+ if (streq_ptr(tz, server->timezone))
+ return 0;
+
+ r = free_and_strdup(&server->timezone, tz);
+ if (r < 0)
+ return r;
+
+ return 1;
+}
+
+int sd_dhcp_server_set_max_lease_time(sd_dhcp_server *server, uint32_t t) {
+ assert_return(server, -EINVAL);
+
+ if (t == server->max_lease_time)
+ return 0;
+
+ server->max_lease_time = t;
+ return 1;
+}
+
+int sd_dhcp_server_set_default_lease_time(sd_dhcp_server *server, uint32_t t) {
+ assert_return(server, -EINVAL);
+
+ if (t == server->default_lease_time)
+ return 0;
+
+ server->default_lease_time = t;
+ return 1;
+}
+
+int sd_dhcp_server_set_dns(sd_dhcp_server *server, const struct in_addr dns[], unsigned n) {
+ assert_return(server, -EINVAL);
+ assert_return(dns || n <= 0, -EINVAL);
+
+ if (server->n_dns == n &&
+ memcmp(server->dns, dns, sizeof(struct in_addr) * n) == 0)
+ return 0;
+
+ if (n <= 0) {
+ server->dns = mfree(server->dns);
+ server->n_dns = 0;
+ } else {
+ struct in_addr *c;
+
+ c = newdup(struct in_addr, dns, n);
+ if (!c)
+ return -ENOMEM;
+
+ free(server->dns);
+ server->dns = c;
+ server->n_dns = n;
+ }
+
+ return 1;
+}
+
+int sd_dhcp_server_set_ntp(sd_dhcp_server *server, const struct in_addr ntp[], unsigned n) {
+ assert_return(server, -EINVAL);
+ assert_return(ntp || n <= 0, -EINVAL);
+
+ if (server->n_ntp == n &&
+ memcmp(server->ntp, ntp, sizeof(struct in_addr) * n) == 0)
+ return 0;
+
+ if (n <= 0) {
+ server->ntp = mfree(server->ntp);
+ server->n_ntp = 0;
+ } else {
+ struct in_addr *c;
+
+ c = newdup(struct in_addr, ntp, n);
+ if (!c)
+ return -ENOMEM;
+
+ free(server->ntp);
+ server->ntp = c;
+ server->n_ntp = n;
+ }
+
+ return 1;
+}
+
+int sd_dhcp_server_set_emit_router(sd_dhcp_server *server, int enabled) {
+ assert_return(server, -EINVAL);
+
+ if (enabled == server->emit_router)
+ return 0;
+
+ server->emit_router = enabled;
+
+ return 1;
+}
diff --git a/src/libsystemd-network/sd-dhcp6-client.c b/src/libsystemd-network/sd-dhcp6-client.c
index 85162dc555..463fde401c 100644
--- a/src/libsystemd-network/sd-dhcp6-client.c
+++ b/src/libsystemd-network/sd-dhcp6-client.c
@@ -1,9 +1,7 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
/***
This file is part of systemd.
- Copyright (C) 2014 Intel Corporation. All rights reserved.
+ Copyright (C) 2014-2015 Intel Corporation. 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
@@ -24,28 +22,31 @@
#include <sys/ioctl.h>
#include <linux/if_infiniband.h>
-#include "udev.h"
-#include "udev-util.h"
-#include "util.h"
-#include "refcnt.h"
-#include "random-util.h"
-
-#include "network-internal.h"
#include "sd-dhcp6-client.h"
-#include "dhcp6-protocol.h"
+
+#include "alloc-util.h"
+#include "dhcp-identifier.h"
#include "dhcp6-internal.h"
#include "dhcp6-lease-internal.h"
-#include "dhcp-identifier.h"
+#include "dhcp6-protocol.h"
+#include "fd-util.h"
+#include "in-addr-util.h"
+#include "network-internal.h"
+#include "random-util.h"
+#include "socket-util.h"
+#include "string-table.h"
+#include "util.h"
#define MAX_MAC_ADDR_LEN INFINIBAND_ALEN
struct sd_dhcp6_client {
- RefCount n_ref;
+ unsigned n_ref;
enum DHCP6State state;
sd_event *event;
int event_priority;
- int index;
+ int ifindex;
+ struct in6_addr local_address;
uint8_t mac_addr[MAX_MAC_ADDR_LEN];
size_t mac_addr_len;
uint16_t arp_type;
@@ -63,16 +64,17 @@ struct sd_dhcp6_client {
uint8_t retransmit_count;
sd_event_source *timeout_resend;
sd_event_source *timeout_resend_expire;
- sd_dhcp6_client_cb_t cb;
+ sd_dhcp6_client_callback_t callback;
void *userdata;
struct duid duid;
size_t duid_len;
};
static const uint16_t default_req_opts[] = {
- DHCP6_OPTION_DNS_SERVERS,
- DHCP6_OPTION_DOMAIN_LIST,
- DHCP6_OPTION_NTP_SERVER,
+ SD_DHCP6_OPTION_DNS_SERVERS,
+ SD_DHCP6_OPTION_DOMAIN_LIST,
+ SD_DHCP6_OPTION_NTP_SERVER,
+ SD_DHCP6_OPTION_SNTP_SERVERS,
};
const char * dhcp6_message_type_table[_DHCP6_MESSAGE_MAX] = {
@@ -104,43 +106,61 @@ const char * dhcp6_message_status_table[_DHCP6_STATUS_MAX] = {
DEFINE_STRING_TABLE_LOOKUP(dhcp6_message_status, int);
-DEFINE_TRIVIAL_CLEANUP_FUNC(sd_dhcp6_client*, sd_dhcp6_client_unref);
-#define _cleanup_dhcp6_client_unref_ _cleanup_(sd_dhcp6_client_unrefp)
-
#define DHCP6_CLIENT_DONT_DESTROY(client) \
- _cleanup_dhcp6_client_unref_ _unused_ sd_dhcp6_client *_dont_destroy_##client = sd_dhcp6_client_ref(client)
+ _cleanup_(sd_dhcp6_client_unrefp) _unused_ sd_dhcp6_client *_dont_destroy_##client = sd_dhcp6_client_ref(client)
static int client_start(sd_dhcp6_client *client, enum DHCP6State state);
-int sd_dhcp6_client_set_callback(sd_dhcp6_client *client,
- sd_dhcp6_client_cb_t cb, void *userdata)
-{
+int sd_dhcp6_client_set_callback(
+ sd_dhcp6_client *client,
+ sd_dhcp6_client_callback_t cb,
+ void *userdata) {
+
assert_return(client, -EINVAL);
- client->cb = cb;
+ client->callback = cb;
client->userdata = userdata;
return 0;
}
-int sd_dhcp6_client_set_index(sd_dhcp6_client *client, int interface_index)
-{
+int sd_dhcp6_client_set_ifindex(sd_dhcp6_client *client, int ifindex) {
+
assert_return(client, -EINVAL);
- assert_return(interface_index >= -1, -EINVAL);
+ assert_return(ifindex >= -1, -EINVAL);
+ assert_return(IN_SET(client->state, DHCP6_STATE_STOPPED), -EBUSY);
- client->index = interface_index;
+ client->ifindex = ifindex;
+ return 0;
+}
+
+int sd_dhcp6_client_set_local_address(
+ sd_dhcp6_client *client,
+ const struct in6_addr *local_address) {
+
+ assert_return(client, -EINVAL);
+ assert_return(local_address, -EINVAL);
+ assert_return(in_addr_is_link_local(AF_INET6, (const union in_addr_union *) local_address) > 0, -EINVAL);
+
+ assert_return(IN_SET(client->state, DHCP6_STATE_STOPPED), -EBUSY);
+
+ client->local_address = *local_address;
return 0;
}
-int sd_dhcp6_client_set_mac(sd_dhcp6_client *client, const uint8_t *addr,
- size_t addr_len, uint16_t arp_type)
-{
+int sd_dhcp6_client_set_mac(
+ sd_dhcp6_client *client,
+ const uint8_t *addr, size_t addr_len,
+ uint16_t arp_type) {
+
assert_return(client, -EINVAL);
assert_return(addr, -EINVAL);
assert_return(addr_len > 0 && addr_len <= MAX_MAC_ADDR_LEN, -EINVAL);
assert_return(arp_type > 0, -EINVAL);
+ assert_return(IN_SET(client->state, DHCP6_STATE_STOPPED), -EBUSY);
+
if (arp_type == ARPHRD_ETHER)
assert_return(addr_len == ETH_ALEN, -EINVAL);
else if (arp_type == ARPHRD_INFINIBAND)
@@ -159,60 +179,68 @@ int sd_dhcp6_client_set_mac(sd_dhcp6_client *client, const uint8_t *addr,
return 0;
}
-static int client_ensure_duid(sd_dhcp6_client *client)
-{
+static int client_ensure_duid(sd_dhcp6_client *client) {
if (client->duid_len != 0)
return 0;
+
return dhcp_identifier_set_duid_en(&client->duid, &client->duid_len);
}
-int sd_dhcp6_client_set_duid(sd_dhcp6_client *client, uint16_t type, uint8_t *duid,
- size_t duid_len)
-{
+/**
+ * Sets DUID. If duid is non-null, the DUID is set to duid_type + duid
+ * without further modification. Otherwise, if duid_type is supported, DUID
+ * is set based on that type. Otherwise, an error is returned.
+ */
+int sd_dhcp6_client_set_duid(
+ sd_dhcp6_client *client,
+ uint16_t duid_type,
+ const void *duid,
+ size_t duid_len) {
+
+ int r;
assert_return(client, -EINVAL);
- assert_return(duid, -EINVAL);
- assert_return(duid_len > 0 && duid_len <= MAX_DUID_LEN, -EINVAL);
+ assert_return(duid_len == 0 || duid != NULL, -EINVAL);
+ assert_return(IN_SET(client->state, DHCP6_STATE_STOPPED), -EBUSY);
- switch (type) {
- case DHCP6_DUID_LLT:
- if (duid_len <= sizeof(client->duid.llt))
- return -EINVAL;
- break;
- case DHCP6_DUID_EN:
- if (duid_len != sizeof(client->duid.en))
- return -EINVAL;
- break;
- case DHCP6_DUID_LL:
- if (duid_len <= sizeof(client->duid.ll))
- return -EINVAL;
- break;
- case DHCP6_DUID_UUID:
- if (duid_len != sizeof(client->duid.uuid))
- return -EINVAL;
- break;
- default:
- /* accept unknown type in order to be forward compatible */
- break;
+ if (duid != NULL) {
+ r = dhcp_validate_duid_len(duid_type, duid_len);
+ if (r < 0)
+ return r;
}
- client->duid.type = htobe16(type);
- memcpy(&client->duid.raw.data, duid, duid_len);
- client->duid_len = duid_len + sizeof(client->duid.type);
+ if (duid != NULL) {
+ client->duid.type = htobe16(duid_type);
+ memcpy(&client->duid.raw.data, duid, duid_len);
+ client->duid_len = sizeof(client->duid.type) + duid_len;
+ } else if (duid_type == DUID_TYPE_EN) {
+ r = dhcp_identifier_set_duid_en(&client->duid, &client->duid_len);
+ if (r < 0)
+ return r;
+ } else
+ return -EOPNOTSUPP;
return 0;
}
-int sd_dhcp6_client_set_information_request(sd_dhcp6_client *client,
- bool enabled) {
+int sd_dhcp6_client_set_iaid(sd_dhcp6_client *client, uint32_t iaid) {
assert_return(client, -EINVAL);
+ assert_return(IN_SET(client->state, DHCP6_STATE_STOPPED), -EBUSY);
+
+ client->ia_na.id = htobe32(iaid);
+
+ return 0;
+}
+
+int sd_dhcp6_client_set_information_request(sd_dhcp6_client *client, int enabled) {
+ assert_return(client, -EINVAL);
+ assert_return(IN_SET(client->state, DHCP6_STATE_STOPPED), -EBUSY);
client->information_request = enabled;
return 0;
}
-int sd_dhcp6_client_get_information_request(sd_dhcp6_client *client,
- bool *enabled) {
+int sd_dhcp6_client_get_information_request(sd_dhcp6_client *client, int *enabled) {
assert_return(client, -EINVAL);
assert_return(enabled, -EINVAL);
@@ -221,18 +249,18 @@ int sd_dhcp6_client_get_information_request(sd_dhcp6_client *client,
return 0;
}
-int sd_dhcp6_client_set_request_option(sd_dhcp6_client *client,
- uint16_t option) {
+int sd_dhcp6_client_set_request_option(sd_dhcp6_client *client, uint16_t option) {
size_t t;
assert_return(client, -EINVAL);
assert_return(client->state == DHCP6_STATE_STOPPED, -EBUSY);
switch(option) {
- case DHCP6_OPTION_DNS_SERVERS:
- case DHCP6_OPTION_DOMAIN_LIST:
- case DHCP6_OPTION_SNTP_SERVERS:
- case DHCP6_OPTION_NTP_SERVER:
+
+ case SD_DHCP6_OPTION_DNS_SERVERS:
+ case SD_DHCP6_OPTION_DOMAIN_LIST:
+ case SD_DHCP6_OPTION_SNTP_SERVERS:
+ case SD_DHCP6_OPTION_NTP_SERVER:
break;
default:
@@ -254,23 +282,38 @@ int sd_dhcp6_client_set_request_option(sd_dhcp6_client *client,
int sd_dhcp6_client_get_lease(sd_dhcp6_client *client, sd_dhcp6_lease **ret) {
assert_return(client, -EINVAL);
- assert_return(ret, -EINVAL);
if (!client->lease)
return -ENOMSG;
- *ret = sd_dhcp6_lease_ref(client->lease);
+ if (ret)
+ *ret = client->lease;
return 0;
}
static void client_notify(sd_dhcp6_client *client, int event) {
- if (client->cb)
- client->cb(client, event, client->userdata);
+ assert(client);
+
+ if (client->callback)
+ client->callback(client, event, client->userdata);
+}
+
+static void client_set_lease(sd_dhcp6_client *client, sd_dhcp6_lease *lease) {
+ assert(client);
+
+ if (client->lease) {
+ dhcp6_lease_clear_timers(&client->lease->ia);
+ sd_dhcp6_lease_unref(client->lease);
+ }
+
+ client->lease = lease;
}
static int client_reset(sd_dhcp6_client *client) {
- assert_return(client, -EINVAL);
+ assert(client);
+
+ client_set_lease(client, NULL);
client->receive_message =
sd_event_source_unref(client->receive_message);
@@ -316,6 +359,8 @@ static int client_send_message(sd_dhcp6_client *client, usec_t time_now) {
usec_t elapsed_usec;
be16_t elapsed_time;
+ assert(client);
+
len = sizeof(DHCP6Message) + optlen;
message = malloc0(len);
@@ -336,7 +381,7 @@ static int client_send_message(sd_dhcp6_client *client, usec_t time_now) {
message->type = DHCP6_SOLICIT;
r = dhcp6_option_append(&opt, &optlen,
- DHCP6_OPTION_RAPID_COMMIT, 0, NULL);
+ SD_DHCP6_OPTION_RAPID_COMMIT, 0, NULL);
if (r < 0)
return r;
@@ -354,7 +399,7 @@ static int client_send_message(sd_dhcp6_client *client, usec_t time_now) {
else
message->type = DHCP6_RENEW;
- r = dhcp6_option_append(&opt, &optlen, DHCP6_OPTION_SERVERID,
+ r = dhcp6_option_append(&opt, &optlen, SD_DHCP6_OPTION_SERVERID,
client->lease->serverid_len,
client->lease->serverid);
if (r < 0)
@@ -380,14 +425,14 @@ static int client_send_message(sd_dhcp6_client *client, usec_t time_now) {
return -EINVAL;
}
- r = dhcp6_option_append(&opt, &optlen, DHCP6_OPTION_ORO,
+ r = dhcp6_option_append(&opt, &optlen, SD_DHCP6_OPTION_ORO,
client->req_opts_len * sizeof(be16_t),
client->req_opts);
if (r < 0)
return r;
assert (client->duid_len);
- r = dhcp6_option_append(&opt, &optlen, DHCP6_OPTION_CLIENTID,
+ r = dhcp6_option_append(&opt, &optlen, SD_DHCP6_OPTION_CLIENTID,
client->duid_len, &client->duid);
if (r < 0)
return r;
@@ -398,7 +443,7 @@ static int client_send_message(sd_dhcp6_client *client, usec_t time_now) {
else
elapsed_time = 0xffff;
- r = dhcp6_option_append(&opt, &optlen, DHCP6_OPTION_ELAPSED_TIME,
+ r = dhcp6_option_append(&opt, &optlen, SD_DHCP6_OPTION_ELAPSED_TIME,
sizeof(elapsed_time), &elapsed_time);
if (r < 0)
return r;
@@ -414,13 +459,12 @@ static int client_send_message(sd_dhcp6_client *client, usec_t time_now) {
return 0;
}
-static int client_timeout_t2(sd_event_source *s, uint64_t usec,
- void *userdata) {
+static int client_timeout_t2(sd_event_source *s, uint64_t usec, void *userdata) {
sd_dhcp6_client *client = userdata;
- assert_return(s, -EINVAL);
- assert_return(client, -EINVAL);
- assert_return(client->lease, -EINVAL);
+ assert(s);
+ assert(client);
+ assert(client->lease);
client->lease->ia.timeout_t2 =
sd_event_source_unref(client->lease->ia.timeout_t2);
@@ -432,13 +476,12 @@ static int client_timeout_t2(sd_event_source *s, uint64_t usec,
return 0;
}
-static int client_timeout_t1(sd_event_source *s, uint64_t usec,
- void *userdata) {
+static int client_timeout_t1(sd_event_source *s, uint64_t usec, void *userdata) {
sd_dhcp6_client *client = userdata;
- assert_return(s, -EINVAL);
- assert_return(client, -EINVAL);
- assert_return(client->lease, -EINVAL);
+ assert(s);
+ assert(client);
+ assert(client->lease);
client->lease->ia.timeout_t1 =
sd_event_source_unref(client->lease->ia.timeout_t1);
@@ -450,8 +493,7 @@ static int client_timeout_t1(sd_event_source *s, uint64_t usec,
return 0;
}
-static int client_timeout_resend_expire(sd_event_source *s, uint64_t usec,
- void *userdata) {
+static int client_timeout_resend_expire(sd_event_source *s, uint64_t usec, void *userdata) {
sd_dhcp6_client *client = userdata;
DHCP6_CLIENT_DONT_DESTROY(client);
enum DHCP6State state;
@@ -462,7 +504,7 @@ static int client_timeout_resend_expire(sd_event_source *s, uint64_t usec,
state = client->state;
- client_stop(client, DHCP6_EVENT_RESEND_EXPIRE);
+ client_stop(client, SD_DHCP6_CLIENT_EVENT_RESEND_EXPIRE);
/* RFC 3315, section 18.1.4., says that "...the client may choose to
use a Solicit message to locate a new DHCP server..." */
@@ -477,8 +519,7 @@ static usec_t client_timeout_compute_random(usec_t val) {
(random_u32() % (2 * USEC_PER_SEC)) * val / 10 / USEC_PER_SEC;
}
-static int client_timeout_resend(sd_event_source *s, uint64_t usec,
- void *userdata) {
+static int client_timeout_resend(sd_event_source *s, uint64_t usec, void *userdata) {
int r = 0;
sd_dhcp6_client *client = userdata;
usec_t time_now, init_retransmit_time = 0, max_retransmit_time = 0;
@@ -552,7 +593,7 @@ static int client_timeout_resend(sd_event_source *s, uint64_t usec,
if (max_retransmit_count &&
client->retransmit_count >= max_retransmit_count) {
- client_stop(client, DHCP6_EVENT_RETRANS_MAX);
+ client_stop(client, SD_DHCP6_CLIENT_EVENT_RETRANS_MAX);
return 0;
}
@@ -580,8 +621,7 @@ static int client_timeout_resend(sd_event_source *s, uint64_t usec,
}
log_dhcp6_client(client, "Next retransmission in %s",
- format_timespan(time_string, FORMAT_TIMESPAN_MAX,
- client->retransmit_time, 0));
+ format_timespan(time_string, FORMAT_TIMESPAN_MAX, client->retransmit_time, USEC_PER_SEC));
r = sd_event_add_time(client->event, &client->timeout_resend,
clock_boottime_or_monotonic(),
@@ -639,16 +679,18 @@ static int client_ensure_iaid(sd_dhcp6_client *client) {
if (client->ia_na.id)
return 0;
- r = dhcp_identifier_set_iaid(client->index, client->mac_addr, client->mac_addr_len, &client->ia_na.id);
+ r = dhcp_identifier_set_iaid(client->ifindex, client->mac_addr, client->mac_addr_len, &client->ia_na.id);
if (r < 0)
return r;
return 0;
}
-static int client_parse_message(sd_dhcp6_client *client,
- DHCP6Message *message, size_t len,
- sd_dhcp6_lease *lease) {
+static int client_parse_message(
+ sd_dhcp6_client *client,
+ DHCP6Message *message,
+ size_t len,
+ sd_dhcp6_lease *lease) {
int r;
uint8_t *optval, *option, *id = NULL;
uint16_t optcode, status;
@@ -656,13 +698,18 @@ static int client_parse_message(sd_dhcp6_client *client,
bool clientid = false;
be32_t iaid_lease;
+ assert(client);
+ assert(message);
+ assert(len >= sizeof(DHCP6Message));
+ assert(lease);
+
option = (uint8_t *)message + sizeof(DHCP6Message);
len -= sizeof(DHCP6Message);
while ((r = dhcp6_option_parse(&option, &len, &optcode, &optlen,
&optval)) >= 0) {
switch (optcode) {
- case DHCP6_OPTION_CLIENTID:
+ case SD_DHCP6_OPTION_CLIENTID:
if (clientid) {
log_dhcp6_client(client, "%s contains multiple clientids",
dhcp6_message_type_to_string(message->type));
@@ -680,7 +727,7 @@ static int client_parse_message(sd_dhcp6_client *client,
break;
- case DHCP6_OPTION_SERVERID:
+ case SD_DHCP6_OPTION_SERVERID:
r = dhcp6_lease_get_serverid(lease, &id, &id_len);
if (r >= 0 && id) {
log_dhcp6_client(client, "%s contains multiple serverids",
@@ -694,7 +741,7 @@ static int client_parse_message(sd_dhcp6_client *client,
break;
- case DHCP6_OPTION_PREFERENCE:
+ case SD_DHCP6_OPTION_PREFERENCE:
if (optlen != 1)
return -EINVAL;
@@ -704,7 +751,7 @@ static int client_parse_message(sd_dhcp6_client *client,
break;
- case DHCP6_OPTION_STATUS_CODE:
+ case SD_DHCP6_OPTION_STATUS_CODE:
if (optlen < 2)
return -EINVAL;
@@ -718,7 +765,7 @@ static int client_parse_message(sd_dhcp6_client *client,
break;
- case DHCP6_OPTION_IA_NA:
+ case SD_DHCP6_OPTION_IA_NA:
if (client->state == DHCP6_STATE_INFORMATION_REQUEST) {
log_dhcp6_client(client, "Information request ignoring IA NA option");
@@ -742,13 +789,42 @@ static int client_parse_message(sd_dhcp6_client *client,
break;
- case DHCP6_OPTION_RAPID_COMMIT:
+ case SD_DHCP6_OPTION_RAPID_COMMIT:
r = dhcp6_lease_set_rapid_commit(lease);
if (r < 0)
return r;
break;
+
+ case SD_DHCP6_OPTION_DNS_SERVERS:
+ r = dhcp6_lease_set_dns(lease, optval, optlen);
+ if (r < 0)
+ return r;
+
+ break;
+
+ case SD_DHCP6_OPTION_DOMAIN_LIST:
+ r = dhcp6_lease_set_domains(lease, optval, optlen);
+ if (r < 0)
+ return r;
+
+ break;
+
+ case SD_DHCP6_OPTION_NTP_SERVER:
+ r = dhcp6_lease_set_ntp(lease, optval, optlen);
+ if (r < 0)
+ return r;
+
+ break;
+
+ case SD_DHCP6_OPTION_SNTP_SERVERS:
+ r = dhcp6_lease_set_sntp(lease, optval, optlen);
+ if (r < 0)
+ return r;
+
+ break;
}
+
}
if (r == -ENOMSG)
@@ -770,12 +846,13 @@ static int client_parse_message(sd_dhcp6_client *client,
return r;
}
-static int client_receive_reply(sd_dhcp6_client *client, DHCP6Message *reply,
- size_t len)
-{
- int r;
- _cleanup_dhcp6_lease_free_ sd_dhcp6_lease *lease = NULL;
+static int client_receive_reply(sd_dhcp6_client *client, DHCP6Message *reply, size_t len) {
+ _cleanup_(sd_dhcp6_lease_unrefp) sd_dhcp6_lease *lease = NULL;
bool rapid_commit;
+ int r;
+
+ assert(client);
+ assert(reply);
if (reply->type != DHCP6_REPLY)
return 0;
@@ -797,24 +874,16 @@ static int client_receive_reply(sd_dhcp6_client *client, DHCP6Message *reply,
return 0;
}
- if (client->lease) {
- dhcp6_lease_clear_timers(&client->lease->ia);
- client->lease = sd_dhcp6_lease_unref(client->lease);
- }
-
- if (client->state != DHCP6_STATE_INFORMATION_REQUEST) {
- client->lease = lease;
- lease = NULL;
- }
+ client_set_lease(client, lease);
+ lease = NULL;
return DHCP6_STATE_BOUND;
}
-static int client_receive_advertise(sd_dhcp6_client *client,
- DHCP6Message *advertise, size_t len) {
- int r;
- _cleanup_dhcp6_lease_free_ sd_dhcp6_lease *lease = NULL;
+static int client_receive_advertise(sd_dhcp6_client *client, DHCP6Message *advertise, size_t len) {
+ _cleanup_(sd_dhcp6_lease_unrefp) sd_dhcp6_lease *lease = NULL;
uint8_t pref_advertise = 0, pref_lease = 0;
+ int r;
if (advertise->type != DHCP6_ADVERTISE)
return 0;
@@ -834,8 +903,7 @@ static int client_receive_advertise(sd_dhcp6_client *client,
r = dhcp6_lease_get_preference(client->lease, &pref_lease);
if (r < 0 || pref_advertise > pref_lease) {
- sd_dhcp6_lease_unref(client->lease);
- client->lease = lease;
+ client_set_lease(client, lease);
lease = NULL;
r = 0;
}
@@ -846,28 +914,40 @@ static int client_receive_advertise(sd_dhcp6_client *client,
return r;
}
-static int client_receive_message(sd_event_source *s, int fd, uint32_t revents,
- void *userdata) {
+static int client_receive_message(
+ sd_event_source *s,
+ int fd, uint32_t
+ revents,
+ void *userdata) {
+
sd_dhcp6_client *client = userdata;
DHCP6_CLIENT_DONT_DESTROY(client);
- _cleanup_free_ DHCP6Message *message;
- int r, buflen, len;
+ _cleanup_free_ DHCP6Message *message = NULL;
+ ssize_t buflen, len;
+ int r = 0;
assert(s);
assert(client);
assert(client->event);
- r = ioctl(fd, FIONREAD, &buflen);
- if (r < 0 || buflen <= 0)
- buflen = DHCP6_MIN_OPTIONS_SIZE;
+ buflen = next_datagram_size_fd(fd);
+ if (buflen < 0)
+ return buflen;
- message = malloc0(buflen);
+ message = malloc(buflen);
if (!message)
return -ENOMEM;
- len = read(fd, message, buflen);
- if ((size_t)len < sizeof(DHCP6Message)) {
- log_dhcp6_client(client, "could not receive message from UDP socket: %m");
+ len = recv(fd, message, buflen, 0);
+ if (len < 0) {
+ if (errno == EAGAIN || errno == EINTR)
+ return 0;
+
+ return log_dhcp6_client_errno(client, errno, "Could not receive message from UDP socket: %m");
+
+ }
+ if ((size_t) len < sizeof(DHCP6Message)) {
+ log_dhcp6_client(client, "Too small to be DHCP6 message: ignoring");
return 0;
}
@@ -890,8 +970,7 @@ static int client_receive_message(sd_event_source *s, int fd, uint32_t revents,
break;
default:
- log_dhcp6_client(client, "unknown message type %d",
- message->type);
+ log_dhcp6_client(client, "Unknown message type %d", message->type);
return 0;
}
@@ -905,7 +984,7 @@ static int client_receive_message(sd_event_source *s, int fd, uint32_t revents,
if (r < 0)
return 0;
- client_notify(client, DHCP6_EVENT_INFORMATION_REQUEST);
+ client_notify(client, SD_DHCP6_CLIENT_EVENT_INFORMATION_REQUEST);
client_start(client, DHCP6_STATE_STOPPED);
@@ -937,7 +1016,7 @@ static int client_receive_message(sd_event_source *s, int fd, uint32_t revents,
return 0;
}
- client_notify(client, DHCP6_EVENT_IP_ACQUIRE);
+ client_notify(client, SD_DHCP6_CLIENT_EVENT_IP_ACQUIRE);
}
break;
@@ -950,23 +1029,21 @@ static int client_receive_message(sd_event_source *s, int fd, uint32_t revents,
return 0;
}
- if (r >= 0) {
+ if (r >= 0)
log_dhcp6_client(client, "Recv %s",
dhcp6_message_type_to_string(message->type));
- }
return 0;
}
-static int client_start(sd_dhcp6_client *client, enum DHCP6State state)
-{
+static int client_start(sd_dhcp6_client *client, enum DHCP6State state) {
int r;
usec_t timeout, time_now;
char time_string[FORMAT_TIMESPAN_MAX];
assert_return(client, -EINVAL);
assert_return(client->event, -EINVAL);
- assert_return(client->index > 0, -EINVAL);
+ assert_return(client->ifindex > 0, -EINVAL);
assert_return(client->state != state, -EINVAL);
client->timeout_resend_expire =
@@ -975,14 +1052,9 @@ static int client_start(sd_dhcp6_client *client, enum DHCP6State state)
client->retransmit_time = 0;
client->retransmit_count = 0;
- if (client->state == DHCP6_STATE_STOPPED) {
- time_now = now(clock_boottime_or_monotonic());
- } else {
- r = sd_event_now(client->event, clock_boottime_or_monotonic(),
- &time_now);
- if (r < 0)
- return r;
- }
+ r = sd_event_now(client->event, clock_boottime_or_monotonic(), &time_now);
+ if (r < 0)
+ return r;
switch (state) {
case DHCP6_STATE_STOPPED:
@@ -1012,7 +1084,7 @@ static int client_start(sd_dhcp6_client *client, enum DHCP6State state)
if (client->lease->ia.lifetime_t1 == 0xffffffff ||
client->lease->ia.lifetime_t2 == 0xffffffff) {
- log_dhcp6_client(client, "infinite T1 0x%08x or T2 0x%08x",
+ log_dhcp6_client(client, "Infinite T1 0x%08x or T2 0x%08x",
be32toh(client->lease->ia.lifetime_t1),
be32toh(client->lease->ia.lifetime_t2));
@@ -1022,9 +1094,7 @@ static int client_start(sd_dhcp6_client *client, enum DHCP6State state)
timeout = client_timeout_compute_random(be32toh(client->lease->ia.lifetime_t1) * USEC_PER_SEC);
log_dhcp6_client(client, "T1 expires in %s",
- format_timespan(time_string,
- FORMAT_TIMESPAN_MAX,
- timeout, 0));
+ format_timespan(time_string, FORMAT_TIMESPAN_MAX, timeout, USEC_PER_SEC));
r = sd_event_add_time(client->event,
&client->lease->ia.timeout_t1,
@@ -1046,9 +1116,7 @@ static int client_start(sd_dhcp6_client *client, enum DHCP6State state)
timeout = client_timeout_compute_random(be32toh(client->lease->ia.lifetime_t2) * USEC_PER_SEC);
log_dhcp6_client(client, "T2 expires in %s",
- format_timespan(time_string,
- FORMAT_TIMESPAN_MAX,
- timeout, 0));
+ format_timespan(time_string, FORMAT_TIMESPAN_MAX, timeout, USEC_PER_SEC));
r = sd_event_add_time(client->event,
&client->lease->ia.timeout_t2,
@@ -1093,21 +1161,31 @@ static int client_start(sd_dhcp6_client *client, enum DHCP6State state)
return 0;
}
-int sd_dhcp6_client_stop(sd_dhcp6_client *client)
-{
- client_stop(client, DHCP6_EVENT_STOP);
+int sd_dhcp6_client_stop(sd_dhcp6_client *client) {
+ assert_return(client, -EINVAL);
+
+ client_stop(client, SD_DHCP6_CLIENT_EVENT_STOP);
return 0;
}
-int sd_dhcp6_client_start(sd_dhcp6_client *client)
-{
- int r = 0;
+int sd_dhcp6_client_is_running(sd_dhcp6_client *client) {
+ assert_return(client, -EINVAL);
+
+ return client->state != DHCP6_STATE_STOPPED;
+}
+
+int sd_dhcp6_client_start(sd_dhcp6_client *client) {
enum DHCP6State state = DHCP6_STATE_SOLICITATION;
+ int r = 0;
assert_return(client, -EINVAL);
assert_return(client->event, -EINVAL);
- assert_return(client->index > 0, -EINVAL);
+ assert_return(client->ifindex > 0, -EINVAL);
+ assert_return(in_addr_is_link_local(AF_INET6, (const union in_addr_union *) &client->local_address) > 0, -EINVAL);
+
+ if (!IN_SET(client->state, DHCP6_STATE_STOPPED))
+ return -EBUSY;
r = client_reset(client);
if (r < 0)
@@ -1121,9 +1199,14 @@ int sd_dhcp6_client_start(sd_dhcp6_client *client)
if (r < 0)
return r;
- r = dhcp6_network_bind_udp_socket(client->index, NULL);
- if (r < 0)
- return r;
+ r = dhcp6_network_bind_udp_socket(client->ifindex, &client->local_address);
+ if (r < 0) {
+ _cleanup_free_ char *p = NULL;
+
+ (void) in_addr_to_string(AF_INET6, (const union in_addr_union*) &client->local_address, &p);
+ return log_dhcp6_client_errno(client, r,
+ "Failed to bind to UDP socket at address %s: %m", strna(p));
+ }
client->fd = r;
@@ -1139,7 +1222,7 @@ int sd_dhcp6_client_start(sd_dhcp6_client *client)
goto error;
r = sd_event_source_set_description(client->receive_message,
- "dhcp6-receive-message");
+ "dhcp6-receive-message");
if (r < 0)
goto error;
@@ -1147,8 +1230,8 @@ int sd_dhcp6_client_start(sd_dhcp6_client *client)
state = DHCP6_STATE_INFORMATION_REQUEST;
log_dhcp6_client(client, "Started in %s mode",
- client->information_request? "Information request":
- "Managed");
+ client->information_request? "Information request":
+ "Managed");
return client_start(client, state);
@@ -1157,9 +1240,7 @@ error:
return r;
}
-int sd_dhcp6_client_attach_event(sd_dhcp6_client *client, sd_event *event,
- int priority)
-{
+int sd_dhcp6_client_attach_event(sd_dhcp6_client *client, sd_event *event, int64_t priority) {
int r;
assert_return(client, -EINVAL);
@@ -1187,38 +1268,45 @@ int sd_dhcp6_client_detach_event(sd_dhcp6_client *client) {
}
sd_event *sd_dhcp6_client_get_event(sd_dhcp6_client *client) {
- if (!client)
- return NULL;
+ assert_return(client, NULL);
return client->event;
}
sd_dhcp6_client *sd_dhcp6_client_ref(sd_dhcp6_client *client) {
- if (client)
- assert_se(REFCNT_INC(client->n_ref) >= 2);
+
+ if (!client)
+ return NULL;
+
+ assert(client->n_ref >= 1);
+ client->n_ref++;
return client;
}
sd_dhcp6_client *sd_dhcp6_client_unref(sd_dhcp6_client *client) {
- if (client && REFCNT_DEC(client->n_ref) == 0) {
- client_reset(client);
- sd_dhcp6_client_detach_event(client);
- sd_dhcp6_lease_unref(client->lease);
+ if (!client)
+ return NULL;
- free(client->req_opts);
- free(client);
+ assert(client->n_ref >= 1);
+ client->n_ref--;
+ if (client->n_ref > 0)
return NULL;
- }
- return client;
+ client_reset(client);
+
+ sd_dhcp6_client_detach_event(client);
+
+ free(client->req_opts);
+ free(client);
+
+ return NULL;
}
-int sd_dhcp6_client_new(sd_dhcp6_client **ret)
-{
- _cleanup_dhcp6_client_unref_ sd_dhcp6_client *client = NULL;
+int sd_dhcp6_client_new(sd_dhcp6_client **ret) {
+ _cleanup_(sd_dhcp6_client_unrefp) sd_dhcp6_client *client = NULL;
size_t t;
assert_return(ret, -EINVAL);
@@ -1227,16 +1315,12 @@ int sd_dhcp6_client_new(sd_dhcp6_client **ret)
if (!client)
return -ENOMEM;
- client->n_ref = REFCNT_INIT;
-
- client->ia_na.type = DHCP6_OPTION_IA_NA;
-
- client->index = -1;
-
+ client->n_ref = 1;
+ client->ia_na.type = SD_DHCP6_OPTION_IA_NA;
+ client->ifindex = -1;
client->fd = -1;
client->req_opts_len = ELEMENTSOF(default_req_opts);
-
client->req_opts = new0(be16_t, client->req_opts_len);
if (!client->req_opts)
return -ENOMEM;
diff --git a/src/libsystemd-network/sd-dhcp6-lease.c b/src/libsystemd-network/sd-dhcp6-lease.c
index 2442269a3f..5c10a6326a 100644
--- a/src/libsystemd-network/sd-dhcp6-lease.c
+++ b/src/libsystemd-network/sd-dhcp6-lease.c
@@ -2,7 +2,7 @@
This file is part of systemd.
Copyright (C) 2014 Tom Gundersen
- Copyright (C) 2014 Intel Corporation. All rights reserved.
+ Copyright (C) 2014-2015 Intel Corporation. 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
@@ -20,9 +20,11 @@
#include <errno.h>
-#include "util.h"
-
+#include "alloc-util.h"
#include "dhcp6-lease-internal.h"
+#include "dhcp6-protocol.h"
+#include "strv.h"
+#include "util.h"
int dhcp6_lease_clear_timers(DHCP6IA *ia) {
assert_return(ia, -EINVAL);
@@ -173,20 +175,221 @@ void sd_dhcp6_lease_reset_address_iter(sd_dhcp6_lease *lease) {
lease->addr_iter = lease->ia.addresses;
}
+int dhcp6_lease_set_dns(sd_dhcp6_lease *lease, uint8_t *optval, size_t optlen) {
+ int r;
+
+ assert_return(lease, -EINVAL);
+ assert_return(optval, -EINVAL);
+
+ if (!optlen)
+ return 0;
+
+ r = dhcp6_option_parse_ip6addrs(optval, optlen, &lease->dns,
+ lease->dns_count,
+ &lease->dns_allocated);
+ if (r < 0) {
+ log_dhcp6_client(client, "Invalid DNS server option: %s",
+ strerror(-r));
+
+ return r;
+ }
+
+ lease->dns_count = r;
+
+ return 0;
+}
+
+int sd_dhcp6_lease_get_dns(sd_dhcp6_lease *lease, struct in6_addr **addrs) {
+ assert_return(lease, -EINVAL);
+ assert_return(addrs, -EINVAL);
+
+ if (lease->dns_count) {
+ *addrs = lease->dns;
+ return lease->dns_count;
+ }
+
+ return -ENOENT;
+}
+
+int dhcp6_lease_set_domains(sd_dhcp6_lease *lease, uint8_t *optval,
+ size_t optlen) {
+ int r;
+ char **domains;
+
+ assert_return(lease, -EINVAL);
+ assert_return(optval, -EINVAL);
+
+ if (!optlen)
+ return 0;
+
+ r = dhcp6_option_parse_domainname(optval, optlen, &domains);
+ if (r < 0)
+ return 0;
+
+ free(lease->domains);
+ lease->domains = domains;
+ lease->domains_count = r;
+
+ return r;
+}
+
+int sd_dhcp6_lease_get_domains(sd_dhcp6_lease *lease, char ***domains) {
+ assert_return(lease, -EINVAL);
+ assert_return(domains, -EINVAL);
+
+ if (lease->domains_count) {
+ *domains = lease->domains;
+ return lease->domains_count;
+ }
+
+ return -ENOENT;
+}
+
+int dhcp6_lease_set_ntp(sd_dhcp6_lease *lease, uint8_t *optval, size_t optlen) {
+ int r;
+ uint16_t subopt;
+ size_t sublen;
+ uint8_t *subval;
+
+ assert_return(lease, -EINVAL);
+ assert_return(optval, -EINVAL);
+
+ lease->ntp = mfree(lease->ntp);
+ lease->ntp_count = 0;
+ lease->ntp_allocated = 0;
+
+ while ((r = dhcp6_option_parse(&optval, &optlen, &subopt, &sublen,
+ &subval)) >= 0) {
+ int s;
+ char **servers;
+
+ switch(subopt) {
+ case DHCP6_NTP_SUBOPTION_SRV_ADDR:
+ case DHCP6_NTP_SUBOPTION_MC_ADDR:
+ if (sublen != 16)
+ return 0;
+
+ s = dhcp6_option_parse_ip6addrs(subval, sublen,
+ &lease->ntp,
+ lease->ntp_count,
+ &lease->ntp_allocated);
+ if (s < 0)
+ return s;
+
+ lease->ntp_count = s;
+
+ break;
+
+ case DHCP6_NTP_SUBOPTION_SRV_FQDN:
+ r = dhcp6_option_parse_domainname(subval, sublen,
+ &servers);
+ if (r < 0)
+ return 0;
+
+ lease->ntp_fqdn = strv_free(lease->ntp_fqdn);
+ lease->ntp_fqdn = servers;
+ lease->ntp_fqdn_count = r;
+
+ break;
+ }
+ }
+
+ if (r != -ENOMSG)
+ return r;
+
+ return 0;
+}
+
+int dhcp6_lease_set_sntp(sd_dhcp6_lease *lease, uint8_t *optval, size_t optlen) {
+ int r;
+
+ assert_return(lease, -EINVAL);
+ assert_return(optval, -EINVAL);
+
+ if (!optlen)
+ return 0;
+
+ if (lease->ntp || lease->ntp_fqdn) {
+ log_dhcp6_client(client, "NTP information already provided");
+
+ return 0;
+ }
+
+ log_dhcp6_client(client, "Using deprecated SNTP information");
+
+ r = dhcp6_option_parse_ip6addrs(optval, optlen, &lease->ntp,
+ lease->ntp_count,
+ &lease->ntp_allocated);
+ if (r < 0) {
+ log_dhcp6_client(client, "Invalid SNTP server option: %s",
+ strerror(-r));
+
+ return r;
+ }
+
+ lease->ntp_count = r;
+
+ return 0;
+}
+
+int sd_dhcp6_lease_get_ntp_addrs(sd_dhcp6_lease *lease,
+ struct in6_addr **addrs) {
+ assert_return(lease, -EINVAL);
+ assert_return(addrs, -EINVAL);
+
+ if (lease->ntp_count) {
+ *addrs = lease->ntp;
+ return lease->ntp_count;
+ }
+
+ return -ENOENT;
+}
+
+int sd_dhcp6_lease_get_ntp_fqdn(sd_dhcp6_lease *lease, char ***ntp_fqdn) {
+ assert_return(lease, -EINVAL);
+ assert_return(ntp_fqdn, -EINVAL);
+
+ if (lease->ntp_fqdn_count) {
+ *ntp_fqdn = lease->ntp_fqdn;
+ return lease->ntp_fqdn_count;
+ }
+
+ return -ENOENT;
+}
+
sd_dhcp6_lease *sd_dhcp6_lease_ref(sd_dhcp6_lease *lease) {
- if (lease)
- assert_se(REFCNT_INC(lease->n_ref) >= 2);
+
+ if (!lease)
+ return NULL;
+
+ assert(lease->n_ref >= 1);
+ lease->n_ref++;
return lease;
}
sd_dhcp6_lease *sd_dhcp6_lease_unref(sd_dhcp6_lease *lease) {
- if (lease && REFCNT_DEC(lease->n_ref) == 0) {
- free(lease->serverid);
- dhcp6_lease_free_ia(&lease->ia);
- free(lease);
- }
+ if (!lease)
+ return NULL;
+
+ assert(lease->n_ref >= 1);
+ lease->n_ref--;
+
+ if (lease->n_ref > 0)
+ return NULL;
+
+ free(lease->serverid);
+ dhcp6_lease_free_ia(&lease->ia);
+
+ free(lease->dns);
+
+ lease->domains = strv_free(lease->domains);
+
+ free(lease->ntp);
+
+ lease->ntp_fqdn = strv_free(lease->ntp_fqdn);
+ free(lease);
return NULL;
}
@@ -198,7 +401,7 @@ int dhcp6_lease_new(sd_dhcp6_lease **ret) {
if (!lease)
return -ENOMEM;
- lease->n_ref = REFCNT_INIT;
+ lease->n_ref = 1;
LIST_HEAD_INIT(lease->ia.addresses);
diff --git a/src/libsystemd-network/sd-icmp6-nd.c b/src/libsystemd-network/sd-icmp6-nd.c
deleted file mode 100644
index 2f867e8562..0000000000
--- a/src/libsystemd-network/sd-icmp6-nd.c
+++ /dev/null
@@ -1,715 +0,0 @@
-/***
- This file is part of systemd.
-
- Copyright (C) 2014 Intel Corporation. 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/icmp6.h>
-#include <netinet/ip6.h>
-#include <string.h>
-#include <stdbool.h>
-#include <netinet/in.h>
-#include <sys/ioctl.h>
-
-#include "socket-util.h"
-#include "refcnt.h"
-#include "async.h"
-
-#include "dhcp6-internal.h"
-#include "sd-icmp6-nd.h"
-
-#define ICMP6_ROUTER_SOLICITATION_INTERVAL 4 * USEC_PER_SEC
-#define ICMP6_MAX_ROUTER_SOLICITATIONS 3
-
-enum icmp6_nd_state {
- ICMP6_NEIGHBOR_DISCOVERY_IDLE = 0,
- ICMP6_ROUTER_SOLICITATION_SENT = 10,
- ICMP6_ROUTER_ADVERTISMENT_LISTEN = 11,
-};
-
-#define IP6_MIN_MTU (unsigned)1280
-#define ICMP6_ND_RECV_SIZE (IP6_MIN_MTU - sizeof(struct ip6_hdr))
-#define ICMP6_OPT_LEN_UNITS 8
-
-typedef struct ICMP6Prefix ICMP6Prefix;
-
-struct ICMP6Prefix {
- RefCount n_ref;
-
- LIST_FIELDS(ICMP6Prefix, prefixes);
-
- uint8_t len;
- sd_event_source *timeout_valid;
- struct in6_addr addr;
-};
-
-struct sd_icmp6_nd {
- RefCount n_ref;
-
- enum icmp6_nd_state state;
- sd_event *event;
- int event_priority;
- int index;
- struct ether_addr mac_addr;
- uint32_t mtu;
- ICMP6Prefix *expired_prefix;
- LIST_HEAD(ICMP6Prefix, prefixes);
- int fd;
- sd_event_source *recv;
- sd_event_source *timeout;
- int nd_sent;
- sd_icmp6_nd_callback_t callback;
- void *userdata;
-};
-
-#define log_icmp6_nd(p, fmt, ...) log_internal(LOG_DEBUG, 0, __FILE__, __LINE__, __func__, "ICMPv6 CLIENT: " fmt, ##__VA_ARGS__)
-
-static ICMP6Prefix *icmp6_prefix_unref(ICMP6Prefix *prefix) {
- if (prefix && REFCNT_DEC(prefix->n_ref) <= 0) {
- prefix->timeout_valid =
- sd_event_source_unref(prefix->timeout_valid);
-
- free(prefix);
- }
-
- return NULL;
-}
-
-static int icmp6_prefix_new(ICMP6Prefix **ret) {
- _cleanup_free_ ICMP6Prefix *prefix = NULL;
-
- assert(ret);
-
- prefix = new0(ICMP6Prefix, 1);
- if (!prefix)
- return -ENOMEM;
-
- prefix->n_ref = REFCNT_INIT;
- LIST_INIT(prefixes, prefix);
-
- *ret = prefix;
- prefix = NULL;
-
- return 0;
-}
-
-static void icmp6_nd_notify(sd_icmp6_nd *nd, int event)
-{
- if (nd->callback)
- nd->callback(nd, event, nd->userdata);
-}
-
-int sd_icmp6_nd_set_callback(sd_icmp6_nd *nd, sd_icmp6_nd_callback_t callback,
- void *userdata) {
- assert(nd);
-
- nd->callback = callback;
- nd->userdata = userdata;
-
- return 0;
-}
-
-int sd_icmp6_nd_set_index(sd_icmp6_nd *nd, int interface_index) {
- assert(nd);
- assert(interface_index >= -1);
-
- nd->index = interface_index;
-
- return 0;
-}
-
-int sd_icmp6_nd_set_mac(sd_icmp6_nd *nd, const struct ether_addr *mac_addr) {
- assert(nd);
-
- if (mac_addr)
- memcpy(&nd->mac_addr, mac_addr, sizeof(nd->mac_addr));
- else
- zero(nd->mac_addr);
-
- return 0;
-
-}
-
-int sd_icmp6_nd_attach_event(sd_icmp6_nd *nd, sd_event *event, int priority) {
- int r;
-
- assert_return(nd, -EINVAL);
- assert_return(!nd->event, -EBUSY);
-
- if (event)
- nd->event = sd_event_ref(event);
- else {
- r = sd_event_default(&nd->event);
- if (r < 0)
- return 0;
- }
-
- nd->event_priority = priority;
-
- return 0;
-}
-
-int sd_icmp6_nd_detach_event(sd_icmp6_nd *nd) {
- assert_return(nd, -EINVAL);
-
- nd->event = sd_event_unref(nd->event);
-
- return 0;
-}
-
-sd_event *sd_icmp6_nd_get_event(sd_icmp6_nd *nd) {
- assert(nd);
-
- return nd->event;
-}
-
-sd_icmp6_nd *sd_icmp6_nd_ref(sd_icmp6_nd *nd) {
- assert (nd);
-
- assert_se(REFCNT_INC(nd->n_ref) >= 2);
-
- return nd;
-}
-
-static int icmp6_nd_init(sd_icmp6_nd *nd) {
- assert(nd);
-
- nd->recv = sd_event_source_unref(nd->recv);
- nd->fd = asynchronous_close(nd->fd);
- nd->timeout = sd_event_source_unref(nd->timeout);
-
- return 0;
-}
-
-sd_icmp6_nd *sd_icmp6_nd_unref(sd_icmp6_nd *nd) {
- if (nd && REFCNT_DEC(nd->n_ref) == 0) {
- ICMP6Prefix *prefix, *p;
-
- icmp6_nd_init(nd);
- sd_icmp6_nd_detach_event(nd);
-
- LIST_FOREACH_SAFE(prefixes, prefix, p, nd->prefixes) {
- LIST_REMOVE(prefixes, nd->prefixes, prefix);
-
- prefix = icmp6_prefix_unref(prefix);
- }
-
- free(nd);
- }
-
- return NULL;
-}
-
-DEFINE_TRIVIAL_CLEANUP_FUNC(sd_icmp6_nd*, sd_icmp6_nd_unref);
-#define _cleanup_sd_icmp6_nd_free_ _cleanup_(sd_icmp6_nd_unrefp)
-
-int sd_icmp6_nd_new(sd_icmp6_nd **ret) {
- _cleanup_sd_icmp6_nd_free_ sd_icmp6_nd *nd = NULL;
-
- assert(ret);
-
- nd = new0(sd_icmp6_nd, 1);
- if (!nd)
- return -ENOMEM;
-
- nd->n_ref = REFCNT_INIT;
-
- nd->index = -1;
- nd->fd = -1;
-
- LIST_HEAD_INIT(nd->prefixes);
-
- *ret = nd;
- nd = NULL;
-
- return 0;
-}
-
-int sd_icmp6_ra_get_mtu(sd_icmp6_nd *nd, uint32_t *mtu) {
- assert_return(nd, -EINVAL);
- assert_return(mtu, -EINVAL);
-
- if (nd->mtu == 0)
- return -ENOMSG;
-
- *mtu = nd->mtu;
-
- return 0;
-}
-
-static int icmp6_ra_prefix_timeout(sd_event_source *s, uint64_t usec,
- void *userdata) {
- sd_icmp6_nd *nd = userdata;
- ICMP6Prefix *prefix, *p;
-
- assert(nd);
-
- LIST_FOREACH_SAFE(prefixes, prefix, p, nd->prefixes) {
- if (prefix->timeout_valid != s)
- continue;
-
- log_icmp6_nd(nd, "Prefix expired "SD_ICMP6_ADDRESS_FORMAT_STR"/%d",
- SD_ICMP6_ADDRESS_FORMAT_VAL(prefix->addr),
- prefix->len);
-
- LIST_REMOVE(prefixes, nd->prefixes, prefix);
-
- nd->expired_prefix = prefix;
- icmp6_nd_notify(nd,
- ICMP6_EVENT_ROUTER_ADVERTISMENT_PREFIX_EXPIRED);
- nd->expired_prefix = NULL;
-
- prefix = icmp6_prefix_unref(prefix);
-
- break;
- }
-
- return 0;
-}
-
-static int icmp6_ra_prefix_set_timeout(sd_icmp6_nd *nd,
- ICMP6Prefix *prefix,
- usec_t valid) {
- usec_t time_now;
- int r;
-
- assert_return(prefix, -EINVAL);
-
- r = sd_event_now(nd->event, clock_boottime_or_monotonic(), &time_now);
- if (r < 0)
- return r;
-
- prefix->timeout_valid = sd_event_source_unref(prefix->timeout_valid);
-
- r = sd_event_add_time(nd->event, &prefix->timeout_valid,
- clock_boottime_or_monotonic(), time_now + valid,
- USEC_PER_SEC, icmp6_ra_prefix_timeout, nd);
- if (r < 0)
- goto error;
-
- r = sd_event_source_set_priority(prefix->timeout_valid,
- nd->event_priority);
- if (r < 0)
- goto error;
-
- r = sd_event_source_set_description(prefix->timeout_valid,
- "icmp6-prefix-timeout");
-
-error:
- if (r < 0)
- prefix->timeout_valid =
- sd_event_source_unref(prefix->timeout_valid);
-
- return r;
-}
-
-static int icmp6_prefix_match(const struct in6_addr *prefix, uint8_t prefixlen,
- const struct in6_addr *addr,
- uint8_t addr_prefixlen) {
- uint8_t bytes, mask, len;
-
- assert_return(prefix, -EINVAL);
- assert_return(addr, -EINVAL);
-
- len = MIN(prefixlen, addr_prefixlen);
-
- bytes = len / 8;
- mask = 0xff << (8 - len % 8);
-
- if (memcmp(prefix, addr, bytes) != 0 ||
- (prefix->s6_addr[bytes] & mask) != (addr->s6_addr[bytes] & mask))
- return -EADDRNOTAVAIL;
-
- return 0;
-}
-
-static int icmp6_ra_prefix_match(ICMP6Prefix *head, const struct in6_addr *addr,
- uint8_t addr_len, ICMP6Prefix **result) {
- ICMP6Prefix *prefix;
-
- LIST_FOREACH(prefixes, prefix, head) {
- if (icmp6_prefix_match(&prefix->addr, prefix->len, addr,
- addr_len) >= 0) {
- *result = prefix;
- return 0;
- }
- }
-
- return -EADDRNOTAVAIL;
-}
-
-int sd_icmp6_prefix_match(struct in6_addr *prefix, uint8_t prefixlen,
- struct in6_addr *addr) {
- return icmp6_prefix_match(prefix, prefixlen, addr,
- sizeof(addr->s6_addr) * 8);
-}
-
-int sd_icmp6_ra_get_prefixlen(sd_icmp6_nd *nd, const struct in6_addr *addr,
- uint8_t *prefixlen) {
- int r;
- ICMP6Prefix *prefix;
-
- assert_return(nd, -EINVAL);
- assert_return(addr, -EINVAL);
- assert_return(prefixlen, -EINVAL);
-
- r = icmp6_ra_prefix_match(nd->prefixes, addr,
- sizeof(addr->s6_addr) * 8, &prefix);
- if (r < 0)
- return r;
-
- *prefixlen = prefix->len;
-
- return 0;
-}
-
-int sd_icmp6_ra_get_expired_prefix(sd_icmp6_nd *nd, struct in6_addr **addr,
- uint8_t *prefixlen)
-{
- assert_return(nd, -EINVAL);
- assert_return(addr, -EINVAL);
- assert_return(prefixlen, -EINVAL);
-
- if (!nd->expired_prefix)
- return -EADDRNOTAVAIL;
-
- *addr = &nd->expired_prefix->addr;
- *prefixlen = nd->expired_prefix->len;
-
- return 0;
-}
-
-static int icmp6_ra_prefix_update(sd_icmp6_nd *nd, ssize_t len,
- const struct nd_opt_prefix_info *prefix_opt) {
- int r;
- ICMP6Prefix *prefix;
- uint32_t lifetime;
- char time_string[FORMAT_TIMESPAN_MAX];
-
- assert_return(nd, -EINVAL);
- assert_return(prefix_opt, -EINVAL);
-
- if (len < prefix_opt->nd_opt_pi_len)
- return -ENOMSG;
-
- if (!(prefix_opt->nd_opt_pi_flags_reserved & ND_OPT_PI_FLAG_ONLINK))
- return 0;
-
- lifetime = be32toh(prefix_opt->nd_opt_pi_valid_time);
-
- r = icmp6_ra_prefix_match(nd->prefixes,
- &prefix_opt->nd_opt_pi_prefix,
- prefix_opt->nd_opt_pi_prefix_len, &prefix);
-
- if (r < 0 && r != -EADDRNOTAVAIL)
- return r;
-
- /* if router advertisment prefix valid timeout is zero, the timeout
- callback will be called immediately to clean up the prefix */
-
- if (r == -EADDRNOTAVAIL) {
- r = icmp6_prefix_new(&prefix);
- if (r < 0)
- return r;
-
- prefix->len = prefix_opt->nd_opt_pi_prefix_len;
-
- memcpy(&prefix->addr, &prefix_opt->nd_opt_pi_prefix,
- sizeof(prefix->addr));
-
- log_icmp6_nd(nd, "New prefix "SD_ICMP6_ADDRESS_FORMAT_STR"/%d lifetime %d expires in %s",
- SD_ICMP6_ADDRESS_FORMAT_VAL(prefix->addr),
- prefix->len, lifetime,
- format_timespan(time_string, FORMAT_TIMESPAN_MAX,
- lifetime * USEC_PER_SEC, 0));
-
- LIST_PREPEND(prefixes, nd->prefixes, prefix);
-
- } else {
- if (prefix->len != prefix_opt->nd_opt_pi_prefix_len) {
- uint8_t prefixlen;
-
- prefixlen = MIN(prefix->len, prefix_opt->nd_opt_pi_prefix_len);
-
- log_icmp6_nd(nd, "Prefix length mismatch %d/%d using %d",
- prefix->len,
- prefix_opt->nd_opt_pi_prefix_len,
- prefixlen);
-
- prefix->len = prefixlen;
- }
-
- log_icmp6_nd(nd, "Update prefix "SD_ICMP6_ADDRESS_FORMAT_STR"/%d lifetime %d expires in %s",
- SD_ICMP6_ADDRESS_FORMAT_VAL(prefix->addr),
- prefix->len, lifetime,
- format_timespan(time_string, FORMAT_TIMESPAN_MAX,
- lifetime * USEC_PER_SEC, 0));
- }
-
- r = icmp6_ra_prefix_set_timeout(nd, prefix, lifetime * USEC_PER_SEC);
-
- return r;
-}
-
-static int icmp6_ra_parse(sd_icmp6_nd *nd, struct nd_router_advert *ra,
- ssize_t len) {
- void *opt;
- struct nd_opt_hdr *opt_hdr;
-
- assert_return(nd, -EINVAL);
- assert_return(ra, -EINVAL);
-
- len -= sizeof(*ra);
- if (len < ICMP6_OPT_LEN_UNITS) {
- log_icmp6_nd(nd, "Router Advertisement below minimum length");
-
- return -ENOMSG;
- }
-
- opt = ra + 1;
- opt_hdr = opt;
-
- while (len != 0 && len >= opt_hdr->nd_opt_len * ICMP6_OPT_LEN_UNITS) {
- struct nd_opt_mtu *opt_mtu;
- uint32_t mtu;
- struct nd_opt_prefix_info *opt_prefix;
-
- if (opt_hdr->nd_opt_len == 0)
- return -ENOMSG;
-
- switch (opt_hdr->nd_opt_type) {
- case ND_OPT_MTU:
- opt_mtu = opt;
-
- mtu = be32toh(opt_mtu->nd_opt_mtu_mtu);
-
- if (mtu != nd->mtu) {
- nd->mtu = MAX(mtu, IP6_MIN_MTU);
-
- log_icmp6_nd(nd, "Router Advertisement link MTU %d using %d",
- mtu, nd->mtu);
- }
-
- break;
-
- case ND_OPT_PREFIX_INFORMATION:
- opt_prefix = opt;
-
- icmp6_ra_prefix_update(nd, len, opt_prefix);
-
- break;
- }
-
- len -= opt_hdr->nd_opt_len * ICMP6_OPT_LEN_UNITS;
- opt = (void *)((char *)opt +
- opt_hdr->nd_opt_len * ICMP6_OPT_LEN_UNITS);
- opt_hdr = opt;
- }
-
- if (len > 0)
- log_icmp6_nd(nd, "Router Advertisement contains %zd bytes of trailing garbage", len);
-
- return 0;
-}
-
-static int icmp6_router_advertisment_recv(sd_event_source *s, int fd,
- uint32_t revents, void *userdata)
-{
- sd_icmp6_nd *nd = userdata;
- int r, buflen = 0;
- ssize_t len;
- _cleanup_free_ struct nd_router_advert *ra = NULL;
- int event = ICMP6_EVENT_ROUTER_ADVERTISMENT_NONE;
-
- assert(s);
- assert(nd);
- assert(nd->event);
-
- r = ioctl(fd, FIONREAD, &buflen);
- if (r < 0 || buflen <= 0)
- buflen = ICMP6_ND_RECV_SIZE;
-
- ra = malloc(buflen);
- if (!ra)
- return -ENOMEM;
-
- len = read(fd, ra, buflen);
- if (len < 0) {
- log_icmp6_nd(nd, "Could not receive message from UDP socket: %m");
- return 0;
- }
-
- if (ra->nd_ra_type != ND_ROUTER_ADVERT)
- return 0;
-
- if (ra->nd_ra_code != 0)
- return 0;
-
- nd->timeout = sd_event_source_unref(nd->timeout);
-
- nd->state = ICMP6_ROUTER_ADVERTISMENT_LISTEN;
-
- if (ra->nd_ra_flags_reserved & ND_RA_FLAG_OTHER )
- event = ICMP6_EVENT_ROUTER_ADVERTISMENT_OTHER;
-
- if (ra->nd_ra_flags_reserved & ND_RA_FLAG_MANAGED)
- event = ICMP6_EVENT_ROUTER_ADVERTISMENT_MANAGED;
-
- log_icmp6_nd(nd, "Received Router Advertisement flags %s/%s",
- ra->nd_ra_flags_reserved & ND_RA_FLAG_MANAGED? "MANAGED": "none",
- ra->nd_ra_flags_reserved & ND_RA_FLAG_OTHER? "OTHER": "none");
-
- if (event != ICMP6_EVENT_ROUTER_ADVERTISMENT_NONE) {
- r = icmp6_ra_parse(nd, ra, len);
- if (r < 0) {
- log_icmp6_nd(nd, "Could not parse Router Advertisement: %s",
- strerror(-r));
- return 0;
- }
- }
-
- icmp6_nd_notify(nd, event);
-
- return 0;
-}
-
-static int icmp6_router_solicitation_timeout(sd_event_source *s, uint64_t usec,
- void *userdata)
-{
- sd_icmp6_nd *nd = userdata;
- uint64_t time_now, next_timeout;
- struct ether_addr unset = { };
- struct ether_addr *addr = NULL;
- int r;
-
- assert(s);
- assert(nd);
- assert(nd->event);
-
- nd->timeout = sd_event_source_unref(nd->timeout);
-
- if (nd->nd_sent >= ICMP6_MAX_ROUTER_SOLICITATIONS) {
- icmp6_nd_notify(nd, ICMP6_EVENT_ROUTER_ADVERTISMENT_TIMEOUT);
- nd->state = ICMP6_ROUTER_ADVERTISMENT_LISTEN;
- } else {
- if (memcmp(&nd->mac_addr, &unset, sizeof(struct ether_addr)))
- addr = &nd->mac_addr;
-
- r = dhcp_network_icmp6_send_router_solicitation(nd->fd, addr);
- if (r < 0)
- log_icmp6_nd(nd, "Error sending Router Solicitation");
- else {
- nd->state = ICMP6_ROUTER_SOLICITATION_SENT;
- log_icmp6_nd(nd, "Sent Router Solicitation");
- }
-
- nd->nd_sent++;
-
- r = sd_event_now(nd->event, clock_boottime_or_monotonic(), &time_now);
- if (r < 0) {
- icmp6_nd_notify(nd, r);
- return 0;
- }
-
- next_timeout = time_now + ICMP6_ROUTER_SOLICITATION_INTERVAL;
-
- r = sd_event_add_time(nd->event, &nd->timeout, clock_boottime_or_monotonic(),
- next_timeout, 0,
- icmp6_router_solicitation_timeout, nd);
- if (r < 0) {
- icmp6_nd_notify(nd, r);
- return 0;
- }
-
- r = sd_event_source_set_priority(nd->timeout,
- nd->event_priority);
- if (r < 0) {
- icmp6_nd_notify(nd, r);
- return 0;
- }
-
- r = sd_event_source_set_description(nd->timeout, "icmp6-timeout");
- if (r < 0) {
- icmp6_nd_notify(nd, r);
- return 0;
- }
- }
-
- return 0;
-}
-
-int sd_icmp6_nd_stop(sd_icmp6_nd *nd) {
- assert_return(nd, -EINVAL);
- assert_return(nd->event, -EINVAL);
-
- log_icmp6_nd(client, "Stop ICMPv6");
-
- icmp6_nd_init(nd);
-
- nd->state = ICMP6_NEIGHBOR_DISCOVERY_IDLE;
-
- return 0;
-}
-
-int sd_icmp6_router_solicitation_start(sd_icmp6_nd *nd) {
- int r;
-
- assert(nd);
- assert(nd->event);
-
- if (nd->state != ICMP6_NEIGHBOR_DISCOVERY_IDLE)
- return -EINVAL;
-
- if (nd->index < 1)
- return -EINVAL;
-
- r = dhcp_network_icmp6_bind_router_solicitation(nd->index);
- if (r < 0)
- return r;
-
- nd->fd = r;
-
- r = sd_event_add_io(nd->event, &nd->recv, nd->fd, EPOLLIN,
- icmp6_router_advertisment_recv, nd);
- if (r < 0)
- goto error;
-
- r = sd_event_source_set_priority(nd->recv, nd->event_priority);
- if (r < 0)
- goto error;
-
- r = sd_event_source_set_description(nd->recv, "icmp6-receive-message");
- if (r < 0)
- goto error;
-
- r = sd_event_add_time(nd->event, &nd->timeout, clock_boottime_or_monotonic(),
- 0, 0, icmp6_router_solicitation_timeout, nd);
- if (r < 0)
- goto error;
-
- r = sd_event_source_set_priority(nd->timeout, nd->event_priority);
- if (r < 0)
- goto error;
-
- r = sd_event_source_set_description(nd->timeout, "icmp6-timeout");
-error:
- if (r < 0)
- icmp6_nd_init(nd);
- else
- log_icmp6_nd(client, "Start Router Solicitation");
-
- return r;
-}
diff --git a/src/libsystemd-network/sd-ipv4acd.c b/src/libsystemd-network/sd-ipv4acd.c
new file mode 100644
index 0000000000..662885840f
--- /dev/null
+++ b/src/libsystemd-network/sd-ipv4acd.c
@@ -0,0 +1,526 @@
+/***
+ 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 "sd-ipv4acd.h"
+
+#include "alloc-util.h"
+#include "arp-util.h"
+#include "ether-addr-util.h"
+#include "fd-util.h"
+#include "in-addr-util.h"
+#include "list.h"
+#include "random-util.h"
+#include "siphash24.h"
+#include "string-util.h"
+#include "util.h"
+
+/* Constants from the RFC */
+#define PROBE_WAIT_USEC (1U * USEC_PER_SEC)
+#define PROBE_NUM 3U
+#define PROBE_MIN_USEC (1U * USEC_PER_SEC)
+#define PROBE_MAX_USEC (2U * USEC_PER_SEC)
+#define ANNOUNCE_WAIT_USEC (2U * USEC_PER_SEC)
+#define ANNOUNCE_NUM 2U
+#define ANNOUNCE_INTERVAL_USEC (2U * USEC_PER_SEC)
+#define MAX_CONFLICTS 10U
+#define RATE_LIMIT_INTERVAL_USEC (60U * USEC_PER_SEC)
+#define DEFEND_INTERVAL_USEC (10U * USEC_PER_SEC)
+
+typedef enum IPv4ACDState {
+ IPV4ACD_STATE_INIT,
+ IPV4ACD_STATE_STARTED,
+ 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 {
+ unsigned n_ref;
+
+ IPv4ACDState state;
+ int ifindex;
+ int fd;
+
+ unsigned n_iteration;
+ unsigned n_conflict;
+
+ sd_event_source *receive_message_event_source;
+ sd_event_source *timer_event_source;
+
+ usec_t defend_window;
+ be32_t address;
+
+ /* External */
+ struct ether_addr mac_addr;
+
+ sd_event *event;
+ int event_priority;
+ sd_ipv4acd_callback_t callback;
+ void* userdata;
+};
+
+#define log_ipv4acd_errno(acd, error, fmt, ...) log_internal(LOG_DEBUG, error, __FILE__, __LINE__, __func__, "IPV4ACD: " fmt, ##__VA_ARGS__)
+#define log_ipv4acd(acd, fmt, ...) log_ipv4acd_errno(acd, 0, fmt, ##__VA_ARGS__)
+
+static void ipv4acd_set_state(sd_ipv4acd *acd, IPv4ACDState st, bool reset_counter) {
+ assert(acd);
+ assert(st < _IPV4ACD_STATE_MAX);
+
+ if (st == acd->state && !reset_counter)
+ acd->n_iteration++;
+ else {
+ acd->state = st;
+ acd->n_iteration = 0;
+ }
+}
+
+static void ipv4acd_reset(sd_ipv4acd *acd) {
+ assert(acd);
+
+ acd->timer_event_source = sd_event_source_unref(acd->timer_event_source);
+ acd->receive_message_event_source = sd_event_source_unref(acd->receive_message_event_source);
+
+ acd->fd = safe_close(acd->fd);
+
+ ipv4acd_set_state(acd, IPV4ACD_STATE_INIT, true);
+}
+
+sd_ipv4acd *sd_ipv4acd_ref(sd_ipv4acd *acd) {
+ if (!acd)
+ return NULL;
+
+ assert_se(acd->n_ref >= 1);
+ acd->n_ref++;
+
+ return acd;
+}
+
+sd_ipv4acd *sd_ipv4acd_unref(sd_ipv4acd *acd) {
+ if (!acd)
+ return NULL;
+
+ assert_se(acd->n_ref >= 1);
+ acd->n_ref--;
+
+ if (acd->n_ref > 0)
+ return NULL;
+
+ ipv4acd_reset(acd);
+ sd_ipv4acd_detach_event(acd);
+
+ free(acd);
+
+ return NULL;
+}
+
+int sd_ipv4acd_new(sd_ipv4acd **ret) {
+ _cleanup_(sd_ipv4acd_unrefp) sd_ipv4acd *acd = NULL;
+
+ assert_return(ret, -EINVAL);
+
+ acd = new0(sd_ipv4acd, 1);
+ if (!acd)
+ return -ENOMEM;
+
+ acd->n_ref = 1;
+ acd->state = IPV4ACD_STATE_INIT;
+ acd->ifindex = -1;
+ acd->fd = -1;
+
+ *ret = acd;
+ acd = NULL;
+
+ return 0;
+}
+
+static void ipv4acd_client_notify(sd_ipv4acd *acd, int event) {
+ assert(acd);
+
+ if (!acd->callback)
+ return;
+
+ acd->callback(acd, event, acd->userdata);
+}
+
+int sd_ipv4acd_stop(sd_ipv4acd *acd) {
+ assert_return(acd, -EINVAL);
+
+ ipv4acd_reset(acd);
+
+ log_ipv4acd(acd, "STOPPED");
+
+ ipv4acd_client_notify(acd, SD_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 *acd, usec_t usec, usec_t random_usec) {
+ _cleanup_(sd_event_source_unrefp) sd_event_source *timer = NULL;
+ usec_t next_timeout, time_now;
+ int r;
+
+ assert(acd);
+
+ next_timeout = usec;
+
+ if (random_usec > 0)
+ next_timeout += (usec_t) random_u64() % random_usec;
+
+ assert_se(sd_event_now(acd->event, clock_boottime_or_monotonic(), &time_now) >= 0);
+
+ r = sd_event_add_time(acd->event, &timer, clock_boottime_or_monotonic(), time_now + next_timeout, 0, ipv4acd_on_timeout, acd);
+ if (r < 0)
+ return r;
+
+ r = sd_event_source_set_priority(timer, acd->event_priority);
+ if (r < 0)
+ return r;
+
+ (void) sd_event_source_set_description(timer, "ipv4acd-timer");
+
+ sd_event_source_unref(acd->timer_event_source);
+ acd->timer_event_source = timer;
+ timer = NULL;
+
+ return 0;
+}
+
+static bool ipv4acd_arp_conflict(sd_ipv4acd *acd, struct ether_arp *arp) {
+ assert(acd);
+ assert(arp);
+
+ /* see the BPF */
+ if (memcmp(arp->arp_spa, &acd->address, sizeof(acd->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 *acd = userdata;
+ int r = 0;
+
+ assert(acd);
+
+ switch (acd->state) {
+
+ case IPV4ACD_STATE_STARTED:
+ ipv4acd_set_state(acd, IPV4ACD_STATE_WAITING_PROBE, true);
+
+ if (acd->n_conflict >= MAX_CONFLICTS) {
+ char ts[FORMAT_TIMESPAN_MAX];
+ log_ipv4acd(acd, "Max conflicts reached, delaying by %s", format_timespan(ts, sizeof(ts), RATE_LIMIT_INTERVAL_USEC, 0));
+
+ r = ipv4acd_set_next_wakeup(acd, RATE_LIMIT_INTERVAL_USEC, PROBE_WAIT_USEC);
+ if (r < 0)
+ goto fail;
+
+ acd->n_conflict = 0;
+ } else {
+ r = ipv4acd_set_next_wakeup(acd, 0, PROBE_WAIT_USEC);
+ if (r < 0)
+ goto fail;
+ }
+
+ break;
+
+ case IPV4ACD_STATE_WAITING_PROBE:
+ case IPV4ACD_STATE_PROBING:
+ /* Send a probe */
+ r = arp_send_probe(acd->fd, acd->ifindex, acd->address, &acd->mac_addr);
+ if (r < 0) {
+ log_ipv4acd_errno(acd, r, "Failed to send ARP probe: %m");
+ goto fail;
+ } else {
+ _cleanup_free_ char *address = NULL;
+ union in_addr_union addr = { .in.s_addr = acd->address };
+
+ (void) in_addr_to_string(AF_INET, &addr, &address);
+ log_ipv4acd(acd, "Probing %s", strna(address));
+ }
+
+ if (acd->n_iteration < PROBE_NUM - 2) {
+ ipv4acd_set_state(acd, IPV4ACD_STATE_PROBING, false);
+
+ r = ipv4acd_set_next_wakeup(acd, PROBE_MIN_USEC, (PROBE_MAX_USEC-PROBE_MIN_USEC));
+ if (r < 0)
+ goto fail;
+ } else {
+ ipv4acd_set_state(acd, IPV4ACD_STATE_WAITING_ANNOUNCE, true);
+
+ r = ipv4acd_set_next_wakeup(acd, ANNOUNCE_WAIT_USEC, 0);
+ if (r < 0)
+ goto fail;
+ }
+
+ break;
+
+ case IPV4ACD_STATE_ANNOUNCING:
+ if (acd->n_iteration >= ANNOUNCE_NUM - 1) {
+ ipv4acd_set_state(acd, IPV4ACD_STATE_RUNNING, false);
+ break;
+ }
+
+ /* fall through */
+
+ case IPV4ACD_STATE_WAITING_ANNOUNCE:
+ /* Send announcement packet */
+ r = arp_send_announcement(acd->fd, acd->ifindex, acd->address, &acd->mac_addr);
+ if (r < 0) {
+ log_ipv4acd_errno(acd, r, "Failed to send ARP announcement: %m");
+ goto fail;
+ } else
+ log_ipv4acd(acd, "ANNOUNCE");
+
+ ipv4acd_set_state(acd, IPV4ACD_STATE_ANNOUNCING, false);
+
+ r = ipv4acd_set_next_wakeup(acd, ANNOUNCE_INTERVAL_USEC, 0);
+ if (r < 0)
+ goto fail;
+
+ if (acd->n_iteration == 0) {
+ acd->n_conflict = 0;
+ ipv4acd_client_notify(acd, SD_IPV4ACD_EVENT_BIND);
+ }
+
+ break;
+
+ default:
+ assert_not_reached("Invalid state.");
+ }
+
+ return 0;
+
+fail:
+ sd_ipv4acd_stop(acd);
+ return 0;
+}
+
+static void ipv4acd_on_conflict(sd_ipv4acd *acd) {
+ _cleanup_free_ char *address = NULL;
+ union in_addr_union addr = { .in.s_addr = acd->address };
+
+ assert(acd);
+
+ acd->n_conflict++;
+
+ (void) in_addr_to_string(AF_INET, &addr, &address);
+ log_ipv4acd(acd, "Conflict on %s (%u)", strna(address), acd->n_conflict);
+
+ ipv4acd_reset(acd);
+ ipv4acd_client_notify(acd, SD_IPV4ACD_EVENT_CONFLICT);
+}
+
+static int ipv4acd_on_packet(
+ sd_event_source *s,
+ int fd,
+ uint32_t revents,
+ void *userdata) {
+
+ sd_ipv4acd *acd = userdata;
+ struct ether_arp packet;
+ ssize_t n;
+ int r;
+
+ assert(s);
+ assert(acd);
+ assert(fd >= 0);
+
+ n = recv(fd, &packet, sizeof(struct ether_arp), 0);
+ if (n < 0) {
+ if (errno == EAGAIN || errno == EINTR)
+ return 0;
+
+ log_ipv4acd_errno(acd, errno, "Failed to read ARP packet: %m");
+ goto fail;
+ }
+ if ((size_t) n != sizeof(struct ether_arp)) {
+ log_ipv4acd(acd, "Ignoring too short ARP packet.");
+ return 0;
+ }
+
+ switch (acd->state) {
+
+ case IPV4ACD_STATE_ANNOUNCING:
+ case IPV4ACD_STATE_RUNNING:
+
+ if (ipv4acd_arp_conflict(acd, &packet)) {
+ usec_t ts;
+
+ assert_se(sd_event_now(acd->event, clock_boottime_or_monotonic(), &ts) >= 0);
+
+ /* Defend address */
+ if (ts > acd->defend_window) {
+ acd->defend_window = ts + DEFEND_INTERVAL_USEC;
+ r = arp_send_announcement(acd->fd, acd->ifindex, acd->address, &acd->mac_addr);
+ if (r < 0) {
+ log_ipv4acd_errno(acd, r, "Failed to send ARP announcement: %m");
+ goto fail;
+ } else
+ log_ipv4acd(acd, "DEFEND");
+
+ } else
+ ipv4acd_on_conflict(acd);
+ }
+ 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(acd);
+ break;
+
+ default:
+ assert_not_reached("Invalid state.");
+ }
+
+ return 0;
+
+fail:
+ sd_ipv4acd_stop(acd);
+ return 0;
+}
+
+int sd_ipv4acd_set_ifindex(sd_ipv4acd *acd, int ifindex) {
+ assert_return(acd, -EINVAL);
+ assert_return(ifindex > 0, -EINVAL);
+ assert_return(acd->state == IPV4ACD_STATE_INIT, -EBUSY);
+
+ acd->ifindex = ifindex;
+
+ return 0;
+}
+
+int sd_ipv4acd_set_mac(sd_ipv4acd *acd, const struct ether_addr *addr) {
+ assert_return(acd, -EINVAL);
+ assert_return(addr, -EINVAL);
+ assert_return(acd->state == IPV4ACD_STATE_INIT, -EBUSY);
+
+ acd->mac_addr = *addr;
+
+ return 0;
+}
+
+int sd_ipv4acd_detach_event(sd_ipv4acd *acd) {
+ assert_return(acd, -EINVAL);
+
+ acd->event = sd_event_unref(acd->event);
+
+ return 0;
+}
+
+int sd_ipv4acd_attach_event(sd_ipv4acd *acd, sd_event *event, int64_t priority) {
+ int r;
+
+ assert_return(acd, -EINVAL);
+ assert_return(!acd->event, -EBUSY);
+
+ if (event)
+ acd->event = sd_event_ref(event);
+ else {
+ r = sd_event_default(&acd->event);
+ if (r < 0)
+ return r;
+ }
+
+ acd->event_priority = priority;
+
+ return 0;
+}
+
+int sd_ipv4acd_set_callback(sd_ipv4acd *acd, sd_ipv4acd_callback_t cb, void *userdata) {
+ assert_return(acd, -EINVAL);
+
+ acd->callback = cb;
+ acd->userdata = userdata;
+
+ return 0;
+}
+
+int sd_ipv4acd_set_address(sd_ipv4acd *acd, const struct in_addr *address) {
+ assert_return(acd, -EINVAL);
+ assert_return(address, -EINVAL);
+ assert_return(acd->state == IPV4ACD_STATE_INIT, -EBUSY);
+
+ acd->address = address->s_addr;
+
+ return 0;
+}
+
+int sd_ipv4acd_is_running(sd_ipv4acd *acd) {
+ assert_return(acd, false);
+
+ return acd->state != IPV4ACD_STATE_INIT;
+}
+
+int sd_ipv4acd_start(sd_ipv4acd *acd) {
+ int r;
+
+ assert_return(acd, -EINVAL);
+ assert_return(acd->event, -EINVAL);
+ assert_return(acd->ifindex > 0, -EINVAL);
+ assert_return(acd->address != 0, -EINVAL);
+ assert_return(!ether_addr_is_null(&acd->mac_addr), -EINVAL);
+ assert_return(acd->state == IPV4ACD_STATE_INIT, -EBUSY);
+
+ r = arp_network_bind_raw_socket(acd->ifindex, acd->address, &acd->mac_addr);
+ if (r < 0)
+ return r;
+
+ safe_close(acd->fd);
+ acd->fd = r;
+ acd->defend_window = 0;
+ acd->n_conflict = 0;
+
+ r = sd_event_add_io(acd->event, &acd->receive_message_event_source, acd->fd, EPOLLIN, ipv4acd_on_packet, acd);
+ if (r < 0)
+ goto fail;
+
+ r = sd_event_source_set_priority(acd->receive_message_event_source, acd->event_priority);
+ if (r < 0)
+ goto fail;
+
+ (void) sd_event_source_set_description(acd->receive_message_event_source, "ipv4acd-receive-message");
+
+ r = ipv4acd_set_next_wakeup(acd, 0, 0);
+ if (r < 0)
+ goto fail;
+
+ ipv4acd_set_state(acd, IPV4ACD_STATE_STARTED, true);
+ return 0;
+
+fail:
+ ipv4acd_reset(acd);
+ return r;
+}
diff --git a/src/libsystemd-network/sd-ipv4ll.c b/src/libsystemd-network/sd-ipv4ll.c
index 9e04db96bb..5603a533a5 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
@@ -17,633 +18,329 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <stdlib.h>
+#include <arpa/inet.h>
#include <errno.h>
-#include <string.h>
#include <stdio.h>
-#include <arpa/inet.h>
+#include <stdlib.h>
+#include <string.h>
-#include "util.h"
-#include "siphash24.h"
+#include "sd-ipv4acd.h"
+#include "sd-ipv4ll.h"
+
+#include "alloc-util.h"
+#include "ether-addr-util.h"
+#include "in-addr-util.h"
#include "list.h"
-#include "refcnt.h"
#include "random-util.h"
+#include "siphash24.h"
+#include "sparse-endian.h"
+#include "string-util.h"
+#include "util.h"
-#include "ipv4ll-internal.h"
-#include "sd-ipv4ll.h"
+#define IPV4LL_NETWORK UINT32_C(0xA9FE0000)
+#define IPV4LL_NETMASK UINT32_C(0xFFFF0000)
-/* 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_(sd_ipv4ll_unrefp) _unused_ sd_ipv4ll *_dont_destroy_##ll = sd_ipv4ll_ref(ll)
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;
+ unsigned n_ref;
+
+ sd_ipv4acd *acd;
+
+ be32_t address; /* the address pushed to ACD */
+ struct ether_addr mac;
+
+ struct {
+ le64_t value;
+ le64_t generation;
+ } seed;
+ bool seed_set;
+
/* External */
be32_t claimed_address;
- struct ether_addr mac_addr;
- sd_event *event;
- int event_priority;
- sd_ipv4ll_cb_t cb;
+
+ sd_ipv4ll_callback_t callback;
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) {
+#define log_ipv4ll_errno(ll, error, fmt, ...) log_internal(LOG_DEBUG, error, __FILE__, __LINE__, __func__, "IPV4LL: " fmt, ##__VA_ARGS__)
+#define log_ipv4ll(ll, fmt, ...) log_ipv4ll_errno(ll, 0, fmt, ##__VA_ARGS__)
- assert(ll);
- assert(st < _IPV4LL_STATE_MAX);
-
- if (st == ll->state && !reset_counter) {
- ll->iteration++;
- } else {
- ll->state = st;
- ll->iteration = 0;
- }
-}
+static void ipv4ll_on_acd(sd_ipv4acd *ll, int event, void *userdata);
-static sd_ipv4ll *ipv4ll_client_notify(sd_ipv4ll *ll, int event) {
- assert(ll);
+sd_ipv4ll *sd_ipv4ll_ref(sd_ipv4ll *ll) {
+ if (!ll)
+ return NULL;
- if (ll->cb) {
- ll = sd_ipv4ll_ref(ll);
- ll->cb(ll, event, ll->userdata);
- ll = sd_ipv4ll_unref(ll);
- }
+ assert(ll->n_ref >= 1);
+ ll->n_ref++;
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);
+sd_ipv4ll *sd_ipv4ll_unref(sd_ipv4ll *ll) {
+ if (!ll)
+ return NULL;
- log_ipv4ll(ll, "STOPPED");
+ assert(ll->n_ref >= 1);
+ ll->n_ref--;
- ll = ipv4ll_client_notify(ll, event);
+ if (ll->n_ref > 0)
+ return NULL;
- if (ll) {
- ll->claimed_address = 0;
- ipv4ll_set_state (ll, IPV4LL_STATE_INIT, 1);
- }
+ sd_ipv4acd_unref(ll->acd);
+ free(ll);
- return ll;
+ return NULL;
}
-static int ipv4ll_pick_address(sd_ipv4ll *ll, be32_t *address) {
- be32_t addr;
+int sd_ipv4ll_new(sd_ipv4ll **ret) {
+ _cleanup_(sd_ipv4ll_unrefp) sd_ipv4ll *ll = NULL;
int r;
- int32_t random;
- assert(ll);
- assert(address);
- assert(ll->random_data);
+ assert_return(ret, -EINVAL);
- 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);
+ ll = new0(sd_ipv4ll, 1);
+ if (!ll)
+ return -ENOMEM;
- *address = addr;
- return 0;
-}
+ ll->n_ref = 1;
-static int ipv4ll_timer(sd_event_source *s, uint64_t usec, void *userdata) {
- sd_ipv4ll *ll = (sd_ipv4ll*)userdata;
+ r = sd_ipv4acd_new(&ll->acd);
+ if (r < 0)
+ return r;
- assert(ll);
+ r = sd_ipv4acd_set_callback(ll->acd, ipv4ll_on_acd, ll);
+ if (r < 0)
+ return r;
- ll->next_wakeup_valid = 0;
- ipv4ll_run_state_machine(ll, IPV4LL_TRIGGER_TIMEOUT, NULL);
+ *ret = ll;
+ ll = 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);
+int sd_ipv4ll_stop(sd_ipv4ll *ll) {
+ assert_return(ll, -EINVAL);
- return 0;
+ return sd_ipv4acd_stop(ll->acd);
}
-int sd_ipv4ll_set_index(sd_ipv4ll *ll, int interface_index) {
+int sd_ipv4ll_set_ifindex(sd_ipv4ll *ll, int ifindex) {
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;
+ assert_return(ifindex > 0, -EINVAL);
+ assert_return(sd_ipv4ll_is_running(ll) == 0, -EBUSY);
- return 0;
+ return sd_ipv4acd_set_ifindex(ll->acd, ifindex);
}
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);
+ assert_return(sd_ipv4ll_is_running(ll) == 0, -EBUSY);
- 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);
+ r = sd_ipv4acd_set_mac(ll->acd, addr);
+ if (r < 0)
+ return r;
+ ll->mac = *addr;
return 0;
}
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;
-
+int sd_ipv4ll_attach_event(sd_ipv4ll *ll, sd_event *event, int64_t priority) {
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;
+ return sd_ipv4acd_attach_event(ll->acd, event, priority);
}
-int sd_ipv4ll_set_callback(sd_ipv4ll *ll, sd_ipv4ll_cb_t cb, void *userdata) {
+int sd_ipv4ll_set_callback(sd_ipv4ll *ll, sd_ipv4ll_callback_t cb, void *userdata) {
assert_return(ll, -EINVAL);
- ll->cb = cb;
+ ll->callback = cb;
ll->userdata = userdata;
return 0;
}
-int sd_ipv4ll_get_address(sd_ipv4ll *ll, struct in_addr *address){
+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) {
+ 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;
-
+int sd_ipv4ll_set_address_seed(sd_ipv4ll *ll, uint64_t seed) {
assert_return(ll, -EINVAL);
- assert_return(seed, -EINVAL);
+ assert_return(sd_ipv4ll_is_running(ll) == 0, -EBUSY);
- entropy = *seed;
+ ll->seed.value = htole64(seed);
+ ll->seed_set = true;
- free(ll->random_data);
- free(ll->random_data_state);
+ return 0;
+}
- ll->random_data = new0(struct random_data, 1);
- ll->random_data_state = new0(char, 128);
+int sd_ipv4ll_is_running(sd_ipv4ll *ll) {
+ assert_return(ll, false);
- if (!ll->random_data || !ll->random_data_state) {
- r = -ENOMEM;
- goto error;
- }
+ return sd_ipv4acd_is_running(ll->acd);
+}
- r = initstate_r((unsigned int)entropy, ll->random_data_state, 128, ll->random_data);
- if (r < 0)
- goto error;
+static bool ipv4ll_address_is_valid(const struct in_addr *address) {
+ assert(address);
-error:
- if (r < 0){
- free(ll->random_data);
- free(ll->random_data_state);
- ll->random_data = NULL;
- ll->random_data_state = NULL;
- }
- return r;
+ if (!in_addr_is_link_local(AF_INET, (const union in_addr_union *) address))
+ return false;
+
+ return !IN_SET(be32toh(address->s_addr) & 0x0000FF00U, 0x0000U, 0xFF00U);
}
-bool sd_ipv4ll_is_running(sd_ipv4ll *ll) {
+int sd_ipv4ll_set_address(sd_ipv4ll *ll, const struct in_addr *address) {
+ int r;
+
assert_return(ll, -EINVAL);
+ assert_return(address, -EINVAL);
+ assert_return(ipv4ll_address_is_valid(address), -EINVAL);
- return !IN_SET(ll->state, IPV4LL_STATE_INIT, IPV4LL_STATE_STOPPED);
-}
+ r = sd_ipv4acd_set_address(ll->acd, address);
+ if (r < 0)
+ return r;
-#define HASH_KEY SD_ID128_MAKE(df,04,22,98,3f,ad,14,52,f9,87,2e,d1,9c,70,e2,f2)
+ ll->address = address->s_addr;
-int sd_ipv4ll_start (sd_ipv4ll *ll) {
- int r;
+ return 0;
+}
- 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);
+#define PICK_HASH_KEY SD_ID128_MAKE(15,ac,82,a6,d6,3f,49,78,98,77,5d,0c,69,02,94,0b)
- ll->state = IPV4LL_STATE_INIT;
+static int ipv4ll_pick_address(sd_ipv4ll *ll) {
+ _cleanup_free_ char *address = NULL;
+ be32_t addr;
- r = arp_network_bind_raw_socket(ll->index, &ll->link);
+ assert(ll);
- if (r < 0)
- goto out;
+ do {
+ uint64_t h;
- ll->fd = r;
- ll->conflict = 0;
- ll->defend_window = 0;
- ll->claimed_address = 0;
+ h = siphash24(&ll->seed, sizeof(ll->seed), PICK_HASH_KEY.bytes);
- if (!ll->random_data) {
- uint8_t seed[8];
+ /* Increase the generation counter by one */
+ ll->seed.generation = htole64(le64toh(ll->seed.generation) + 1);
- /* Fallback to mac */
- siphash24(seed, &ll->mac_addr.ether_addr_octet,
- ETH_ALEN, HASH_KEY.bytes);
+ addr = htobe32((h & UINT32_C(0x0000FFFF)) | IPV4LL_NETWORK);
+ } while (addr == ll->address ||
+ IN_SET(be32toh(addr) & 0x0000FF00U, 0x0000U, 0xFF00U));
- r = sd_ipv4ll_set_address_seed(ll, seed);
- if (r < 0)
- goto out;
- }
+ (void) in_addr_to_string(AF_INET, &(union in_addr_union) { .in.s_addr = addr }, &address);
+ log_ipv4ll(ll, "Picked new IP address %s.", strna(address));
- if (ll->address == 0) {
- r = ipv4ll_pick_address(ll, &ll->address);
- if (r < 0)
- goto out;
- }
+ return sd_ipv4ll_set_address(ll, &(struct in_addr) { addr });
+}
- ipv4ll_set_state (ll, IPV4LL_STATE_INIT, 1);
+#define MAC_HASH_KEY SD_ID128_MAKE(df,04,22,98,3f,ad,14,52,f9,87,2e,d1,9c,70,e2,f2)
- r = sd_event_add_io(ll->event, &ll->receive_message, ll->fd,
- EPOLLIN, ipv4ll_receive_message, ll);
- if (r < 0)
- goto out;
+int sd_ipv4ll_start(sd_ipv4ll *ll) {
+ int r;
+ bool picked_address = false;
- r = sd_event_source_set_priority(ll->receive_message, ll->event_priority);
- if (r < 0)
- goto out;
+ assert_return(ll, -EINVAL);
+ assert_return(!ether_addr_is_null(&ll->mac), -EINVAL);
+ assert_return(sd_ipv4ll_is_running(ll) == 0, -EBUSY);
- r = sd_event_source_set_description(ll->receive_message, "ipv4ll-receive-message");
- if (r < 0)
- goto out;
+ /* If no random seed is set, generate some from the MAC address */
+ if (!ll->seed_set)
+ ll->seed.value = htole64(siphash24(ll->mac.ether_addr_octet, ETH_ALEN, MAC_HASH_KEY.bytes));
- r = sd_event_add_time(ll->event,
- &ll->timer,
- clock_boottime_or_monotonic(),
- now(clock_boottime_or_monotonic()), 0,
- ipv4ll_timer, ll);
+ /* Restart the generation counter. */
+ ll->seed.generation = 0;
- if (r < 0)
- goto out;
+ if (ll->address == 0) {
+ r = ipv4ll_pick_address(ll);
+ if (r < 0)
+ return r;
- r = sd_event_source_set_priority(ll->timer, ll->event_priority);
- if (r < 0)
- goto out;
+ picked_address = true;
+ }
- r = sd_event_source_set_description(ll->timer, "ipv4ll-timer");
-out:
- if (r < 0)
- ipv4ll_stop(ll, IPV4LL_EVENT_STOP);
+ r = sd_ipv4acd_start(ll->acd);
+ if (r < 0) {
- return 0;
-}
+ /* We couldn't start? If so, let's forget the picked address again, the user might make a change and
+ * retry, and we want the new data to take effect when picking an address. */
+ if (picked_address)
+ ll->address = 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 r;
+ }
return 0;
}
-sd_ipv4ll *sd_ipv4ll_ref(sd_ipv4ll *ll) {
- if (ll)
- assert_se(REFCNT_INC(ll->n_ref) >= 2);
+static void ipv4ll_client_notify(sd_ipv4ll *ll, int event) {
+ assert(ll);
- return ll;
+ if (ll->callback)
+ ll->callback(ll, event, ll->userdata);
}
-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);
+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) {
- return NULL;
- }
+ case SD_IPV4ACD_EVENT_STOP:
+ ipv4ll_client_notify(ll, SD_IPV4LL_EVENT_STOP);
+ ll->claimed_address = 0;
+ break;
- return ll;
-}
+ case SD_IPV4ACD_EVENT_BIND:
+ ll->claimed_address = ll->address;
+ ipv4ll_client_notify(ll, SD_IPV4LL_EVENT_BIND);
+ break;
-DEFINE_TRIVIAL_CLEANUP_FUNC(sd_ipv4ll*, sd_ipv4ll_unref);
-#define _cleanup_ipv4ll_free_ _cleanup_(sd_ipv4ll_unrefp)
+ case SD_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, SD_IPV4LL_EVENT_CONFLICT);
-int sd_ipv4ll_new(sd_ipv4ll **ret) {
- _cleanup_ipv4ll_free_ sd_ipv4ll *ll = NULL;
+ ll->claimed_address = 0;
+ } else {
+ r = ipv4ll_pick_address(ll);
+ if (r < 0)
+ goto error;
- assert_return(ret, -EINVAL);
+ r = sd_ipv4acd_start(ll->acd);
+ if (r < 0)
+ goto error;
+ }
- ll = new0(sd_ipv4ll, 1);
- if (!ll)
- return -ENOMEM;
+ break;
- ll->n_ref = REFCNT_INIT;
- ll->state = IPV4LL_STATE_INIT;
- ll->index = -1;
- ll->fd = -1;
+ default:
+ assert_not_reached("Invalid IPv4ACD event.");
+ }
- *ret = ll;
- ll = NULL;
+ return;
- return 0;
+error:
+ ipv4ll_client_notify(ll, SD_IPV4LL_EVENT_STOP);
}
diff --git a/src/libsystemd-network/sd-lldp.c b/src/libsystemd-network/sd-lldp.c
index 6a2c05185d..0bd1e66aa0 100644
--- a/src/libsystemd-network/sd-lldp.c
+++ b/src/libsystemd-network/sd-lldp.c
@@ -1,682 +1,539 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
/***
- This file is part of systemd.
+ This file is part of systemd.
- Copyright (C) 2014 Tom Gundersen
- Copyright (C) 2014 Susant Sahani
+ Copyright (C) 2014 Tom Gundersen
+ Copyright (C) 2014 Susant Sahani
- 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 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.
+ 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/>.
+ 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 "siphash24.h"
-#include "hashmap.h"
-
-#include "lldp-tlv.h"
-#include "lldp-port.h"
#include "sd-lldp.h"
-#include "prioq.h"
-#include "lldp-internal.h"
-#include "lldp-util.h"
-
-typedef enum LLDPAgentRXState {
- LLDP_AGENT_RX_WAIT_PORT_OPERATIONAL = 4,
- LLDP_AGENT_RX_DELETE_AGED_INFO,
- LLDP_AGENT_RX_LLDP_INITIALIZE,
- LLDP_AGENT_RX_WAIT_FOR_FRAME,
- LLDP_AGENT_RX_RX_FRAME,
- LLDP_AGENT_RX_DELETE_INFO,
- LLDP_AGENT_RX_UPDATE_INFO,
- _LLDP_AGENT_RX_STATE_MAX,
- _LLDP_AGENT_RX_INVALID = -1,
-} LLDPAgentRXState;
-
-/* Section 10.5.2.2 Reception counters */
-struct lldp_agent_statistics {
- uint64_t stats_ageouts_total;
- uint64_t stats_frames_discarded_total;
- uint64_t stats_frames_in_errors_total;
- uint64_t stats_frames_in_total;
- uint64_t stats_tlvs_discarded_total;
- uint64_t stats_tlvs_unrecognized_total;
-};
-
-struct sd_lldp {
- lldp_port *port;
-
- Prioq *by_expiry;
- Hashmap *neighbour_mib;
-
- sd_lldp_cb_t cb;
-
- void *userdata;
-
- LLDPAgentRXState rx_state;
- lldp_agent_statistics statistics;
-};
-
-static unsigned long chassis_id_hash_func(const void *p,
- const uint8_t hash_key[HASH_KEY_SIZE]) {
- uint64_t u;
- const lldp_chassis_id *id = p;
-
- assert(id);
-
- siphash24((uint8_t *) &u, id->data, id->length, hash_key);
-
- return (unsigned long) u;
-}
-static int chassis_id_compare_func(const void *_a, const void *_b) {
- const lldp_chassis_id *a, *b;
-
- a = _a;
- b = _b;
+#include "alloc-util.h"
+#include "fd-util.h"
+#include "lldp-internal.h"
+#include "lldp-neighbor.h"
+#include "lldp-network.h"
+#include "socket-util.h"
+#include "ether-addr-util.h"
- assert(!a->length || a->data);
- assert(!b->length || b->data);
+#define LLDP_DEFAULT_NEIGHBORS_MAX 128U
- if (a->type != b->type)
- return -1;
+static void lldp_flush_neighbors(sd_lldp *lldp) {
+ sd_lldp_neighbor *n;
- if (a->length != b->length)
- return a->length < b->length ? -1 : 1;
+ assert(lldp);
- return memcmp(a->data, b->data, a->length);
+ while ((n = hashmap_first(lldp->neighbor_by_id)))
+ lldp_neighbor_unlink(n);
}
-static const struct hash_ops chassis_id_hash_ops = {
- .hash = chassis_id_hash_func,
- .compare = chassis_id_compare_func
-};
-
-static void lldp_mib_delete_objects(sd_lldp *lldp);
-static void lldp_set_state(sd_lldp *lldp, LLDPAgentRXState state);
-static void lldp_run_state_machine(sd_lldp *ll);
-
-static int lldp_receive_frame(sd_lldp *lldp, tlv_packet *tlv) {
- int r;
-
+static void lldp_callback(sd_lldp *lldp, sd_lldp_event event, sd_lldp_neighbor *n) {
assert(lldp);
- assert(tlv);
-
- /* Remove expired packets */
- if (prioq_size(lldp->by_expiry) > 0) {
- lldp_set_state(lldp, LLDP_AGENT_RX_DELETE_INFO);
+ log_lldp("Invoking callback for '%c'.", event);
- lldp_mib_delete_objects(lldp);
- }
-
- r = lldp_mib_add_objects(lldp->by_expiry, lldp->neighbour_mib, tlv);
- if (r < 0)
- goto out;
-
- lldp_set_state(lldp, LLDP_AGENT_RX_UPDATE_INFO);
-
- log_lldp("Packet added. MIB size: %d , PQ size: %d",
- hashmap_size(lldp->neighbour_mib),
- prioq_size(lldp->by_expiry));
-
- lldp->statistics.stats_frames_in_total ++;
-
- out:
- if (r < 0)
- log_lldp("Receive frame failed: %s", strerror(-r));
-
- lldp_set_state(lldp, LLDP_AGENT_RX_WAIT_FOR_FRAME);
+ if (!lldp->callback)
+ return;
- return 0;
+ lldp->callback(lldp, event, n, lldp->userdata);
}
-/* 10.3.2 LLDPDU validation: rxProcessFrame() */
-int lldp_handle_packet(tlv_packet *tlv, uint16_t length) {
- uint16_t type, len, i, l, t;
- bool chassis_id = false;
- bool malformed = false;
- bool port_id = false;
- bool ttl = false;
- bool end = false;
- lldp_port *port;
- uint8_t *p, *q;
- sd_lldp *lldp;
- int r;
-
- assert(tlv);
- assert(length > 0);
+static int lldp_make_space(sd_lldp *lldp, size_t extra) {
+ usec_t t = USEC_INFINITY;
+ bool changed = false;
- port = (lldp_port *) tlv->userdata;
- lldp = (sd_lldp *) port->userdata;
-
- if (lldp->port->status == LLDP_PORT_STATUS_DISABLED) {
- log_lldp("Port is disabled : %s . Dropping ...",
- lldp->port->ifname);
- goto out;
- }
-
- lldp_set_state(lldp, LLDP_AGENT_RX_RX_FRAME);
-
- p = tlv->pdu;
- p += sizeof(struct ether_header);
+ assert(lldp);
- for (i = 1, l = 0; l <= length; i++) {
+ /* Remove all entries that are past their TTL, and more until at least the specified number of extra entries
+ * are free. */
- memcpy(&t, p, sizeof(uint16_t));
+ for (;;) {
+ _cleanup_(sd_lldp_neighbor_unrefp) sd_lldp_neighbor *n = NULL;
- type = ntohs(t) >> 9;
- len = ntohs(t) & 0x01ff;
+ n = prioq_peek(lldp->neighbor_by_expiry);
+ if (!n)
+ break;
- if (type == LLDP_TYPE_END) {
- if (len != 0) {
- log_lldp("TLV type end is not length 0. Length:%d received . Dropping ...",
- len);
+ sd_lldp_neighbor_ref(n);
- malformed = true;
- goto out;
- }
+ if (hashmap_size(lldp->neighbor_by_id) > LESS_BY(lldp->neighbors_max, extra))
+ goto remove_one;
- end = true;
+ if (t == USEC_INFINITY)
+ t = now(clock_boottime_or_monotonic());
+ if (n->until > t)
break;
- } else if (type >=_LLDP_TYPE_MAX) {
- log_lldp("TLV type not recognized %d . Dropping ...",
- type);
- malformed = true;
- goto out;
- }
+ remove_one:
+ lldp_neighbor_unlink(n);
+ lldp_callback(lldp, SD_LLDP_EVENT_REMOVED, n);
+ changed = true;
+ }
- /* skip type and lengh encoding */
- p += 2;
- q = p;
+ return changed;
+}
- p += len;
- l += (len + 2);
+static bool lldp_keep_neighbor(sd_lldp *lldp, sd_lldp_neighbor *n) {
+ assert(lldp);
+ assert(n);
- if (i <= 3) {
- if (i != type) {
- log_lldp("TLV missing or out of order. Dropping ...");
+ /* Don't keep data with a zero TTL */
+ if (n->ttl <= 0)
+ return false;
- malformed = true;
- goto out;
- }
- }
+ /* Filter out data from the filter address */
+ if (!ether_addr_is_null(&lldp->filter_address) &&
+ ether_addr_equal(&lldp->filter_address, &n->source_address))
+ return false;
- switch(type) {
- case LLDP_TYPE_CHASSIS_ID:
+ /* Only add if the neighbor has a capability we are interested in. Note that we also store all neighbors with
+ * no caps field set. */
+ if (n->has_capabilities &&
+ (n->enabled_capabilities & lldp->capability_mask) == 0)
+ return false;
- if (len < 2) {
- log_lldp("Received malformed Chassis ID TLV len = %d. Dropping",
- len);
+ /* Keep everything else */
+ return true;
+}
- malformed = true;
- goto out;
- }
+static int lldp_start_timer(sd_lldp *lldp, sd_lldp_neighbor *neighbor);
- if (chassis_id) {
- log_lldp("Duplicate Chassis ID TLV found. Dropping ...");
+static int lldp_add_neighbor(sd_lldp *lldp, sd_lldp_neighbor *n) {
+ _cleanup_(sd_lldp_neighbor_unrefp) sd_lldp_neighbor *old = NULL;
+ bool keep;
+ int r;
- malformed = true;
- goto out;
- }
+ assert(lldp);
+ assert(n);
+ assert(!n->lldp);
- /* Look what subtype it has */
- if (*q == LLDP_CHASSIS_SUBTYPE_RESERVED ||
- *q > LLDP_CHASSIS_SUBTYPE_LOCALLY_ASSIGNED) {
- log_lldp("Unknown subtype: %d found in Chassis ID TLV . Dropping ...",
- *q);
+ keep = lldp_keep_neighbor(lldp, n);
- malformed = true;
- goto out;
+ /* First retrieve the old entry for this MSAP */
+ old = hashmap_get(lldp->neighbor_by_id, &n->id);
+ if (old) {
+ sd_lldp_neighbor_ref(old);
- }
+ if (!keep) {
+ lldp_neighbor_unlink(old);
+ lldp_callback(lldp, SD_LLDP_EVENT_REMOVED, old);
+ return 0;
+ }
- chassis_id = true;
+ if (lldp_neighbor_equal(n, old)) {
+ /* Is this equal, then restart the TTL counter, but don't do anyting else. */
+ old->timestamp = n->timestamp;
+ lldp_start_timer(lldp, old);
+ lldp_callback(lldp, SD_LLDP_EVENT_REFRESHED, old);
+ return 0;
+ }
- break;
- case LLDP_TYPE_PORT_ID:
+ /* Data changed, remove the old entry, and add a new one */
+ lldp_neighbor_unlink(old);
- if (len < 2) {
- log_lldp("Received malformed Port ID TLV len = %d. Dropping",
- len);
+ } else if (!keep)
+ return 0;
- malformed = true;
- goto out;
- }
+ /* Then, make room for at least one new neighbor */
+ lldp_make_space(lldp, 1);
- if (port_id) {
- log_lldp("Duplicate Port ID TLV found. Dropping ...");
+ r = hashmap_put(lldp->neighbor_by_id, &n->id, n);
+ if (r < 0)
+ goto finish;
- malformed = true;
- goto out;
- }
+ r = prioq_put(lldp->neighbor_by_expiry, n, &n->prioq_idx);
+ if (r < 0) {
+ assert_se(hashmap_remove(lldp->neighbor_by_id, &n->id) == n);
+ goto finish;
+ }
- /* Look what subtype it has */
- if (*q == LLDP_PORT_SUBTYPE_RESERVED ||
- *q > LLDP_PORT_SUBTYPE_LOCALLY_ASSIGNED) {
- log_lldp("Unknown subtype: %d found in Port ID TLV . Dropping ...",
- *q);
+ n->lldp = lldp;
- malformed = true;
- goto out;
+ lldp_start_timer(lldp, n);
+ lldp_callback(lldp, old ? SD_LLDP_EVENT_UPDATED : SD_LLDP_EVENT_ADDED, n);
- }
+ return 1;
- port_id = true;
+finish:
+ if (old)
+ lldp_callback(lldp, SD_LLDP_EVENT_REMOVED, old);
- break;
- case LLDP_TYPE_TTL:
+ return r;
+}
- if(len != 2) {
- log_lldp(
- "Received invalid lenth: %d TTL TLV. Dropping ...",
- len);
+static int lldp_handle_datagram(sd_lldp *lldp, sd_lldp_neighbor *n) {
+ int r;
- malformed = true;
- goto out;
- }
+ assert(lldp);
+ assert(n);
- if (ttl) {
- log_lldp("Duplicate TTL TLV found. Dropping ...");
+ r = lldp_neighbor_parse(n);
+ if (r == -EBADMSG) /* Ignore bad messages */
+ return 0;
+ if (r < 0)
+ return r;
- malformed = true;
- goto out;
- }
+ r = lldp_add_neighbor(lldp, n);
+ if (r < 0) {
+ log_lldp_errno(r, "Failed to add datagram. Ignoring.");
+ return 0;
+ }
- ttl = true;
+ log_lldp("Successfully processed LLDP datagram.");
+ return 0;
+}
- break;
- default:
+static int lldp_receive_datagram(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
+ _cleanup_(sd_lldp_neighbor_unrefp) sd_lldp_neighbor *n = NULL;
+ ssize_t space, length;
+ sd_lldp *lldp = userdata;
+ struct timespec ts;
- if (len == 0) {
- log_lldp("TLV type = %d's, length 0 received . Dropping ...",
- type);
+ assert(fd >= 0);
+ assert(lldp);
- malformed = true;
- goto out;
- }
- break;
- }
- }
+ space = next_datagram_size_fd(fd);
+ if (space < 0)
+ return log_lldp_errno(space, "Failed to determine datagram size to read: %m");
- if(!chassis_id || !port_id || !ttl || !end) {
- log_lldp( "One or more mandotory TLV missing . Dropping ...");
+ n = lldp_neighbor_new(space);
+ if (!n)
+ return -ENOMEM;
- malformed = true;
- goto out;
+ length = recv(fd, LLDP_NEIGHBOR_RAW(n), n->raw_size, MSG_DONTWAIT);
+ if (length < 0) {
+ if (errno == EAGAIN || errno == EINTR)
+ return 0;
+ return log_lldp_errno(errno, "Failed to read LLDP datagram: %m");
}
- r = tlv_packet_parse_pdu(tlv, length);
- if (r < 0) {
- log_lldp( "Failed to parse the TLV. Dropping ...");
-
- malformed = true;
- goto out;
+ if ((size_t) length != n->raw_size) {
+ log_lldp("Packet size mismatch.");
+ return -EINVAL;
}
- return lldp_receive_frame(lldp, tlv);
-
- out:
- lldp_set_state(lldp, LLDP_AGENT_RX_WAIT_FOR_FRAME);
+ /* Try to get the timestamp of this packet if it is known */
+ if (ioctl(fd, SIOCGSTAMPNS, &ts) >= 0)
+ triple_timestamp_from_realtime(&n->timestamp, timespec_load(&ts));
+ else
+ triple_timestamp_get(&n->timestamp);
- if (malformed) {
- lldp->statistics.stats_frames_discarded_total ++;
- lldp->statistics.stats_frames_in_errors_total ++;
- }
+ return lldp_handle_datagram(lldp, n);
+}
- tlv_packet_free(tlv);
+static void lldp_reset(sd_lldp *lldp) {
+ assert(lldp);
- return 0;
+ lldp->timer_event_source = sd_event_source_unref(lldp->timer_event_source);
+ lldp->io_event_source = sd_event_source_unref(lldp->io_event_source);
+ lldp->fd = safe_close(lldp->fd);
}
-static int ttl_expiry_item_prioq_compare_func(const void *a, const void *b) {
- const lldp_neighbour_port *p = a, *q = b;
+_public_ int sd_lldp_start(sd_lldp *lldp) {
+ int r;
- if (p->until < q->until)
- return -1;
+ assert_return(lldp, -EINVAL);
+ assert_return(lldp->event, -EINVAL);
+ assert_return(lldp->ifindex > 0, -EINVAL);
- if (p->until > q->until)
- return 1;
+ if (lldp->fd >= 0)
+ return 0;
- return 0;
-}
+ assert(!lldp->io_event_source);
-static void lldp_set_state(sd_lldp *lldp, LLDPAgentRXState state) {
+ lldp->fd = lldp_network_bind_raw_socket(lldp->ifindex);
+ if (lldp->fd < 0)
+ return lldp->fd;
- assert(lldp);
- assert(state < _LLDP_AGENT_RX_STATE_MAX);
+ r = sd_event_add_io(lldp->event, &lldp->io_event_source, lldp->fd, EPOLLIN, lldp_receive_datagram, lldp);
+ if (r < 0)
+ goto fail;
- lldp->rx_state = state;
+ r = sd_event_source_set_priority(lldp->io_event_source, lldp->event_priority);
+ if (r < 0)
+ goto fail;
- lldp_run_state_machine(lldp);
-}
+ (void) sd_event_source_set_description(lldp->io_event_source, "lldp-io");
-static void lldp_run_state_machine(sd_lldp *lldp) {
+ log_lldp("Started LLDP client");
+ return 1;
- if (lldp->rx_state == LLDP_AGENT_RX_UPDATE_INFO)
- if (lldp->cb)
- lldp->cb(lldp, LLDP_AGENT_RX_UPDATE_INFO, lldp->userdata);
+fail:
+ lldp_reset(lldp);
+ return r;
}
-/* 10.5.5.2.1 mibDeleteObjects ()
- * The mibDeleteObjects () procedure deletes all information in the LLDP remote
- * systems MIB associated with the MSAP identifier if an LLDPDU is received with
- * an rxTTL value of zero (see 10.3.2) or the timing counter rxInfoTTL expires. */
-
-static void lldp_mib_delete_objects(sd_lldp *lldp) {
- lldp_neighbour_port *p;
- usec_t t = 0;
+_public_ int sd_lldp_stop(sd_lldp *lldp) {
+ assert_return(lldp, -EINVAL);
- /* Remove all entries that are past their TTL */
- for (;;) {
+ if (lldp->fd < 0)
+ return 0;
- if (prioq_size(lldp->by_expiry) <= 0)
- break;
+ log_lldp("Stopping LLDP client");
- p = prioq_peek(lldp->by_expiry);
- if (!p)
- break;
+ lldp_reset(lldp);
+ lldp_flush_neighbors(lldp);
- if (t <= 0)
- t = now(CLOCK_BOOTTIME);
+ return 1;
+}
- if (p->until > t)
- break;
+_public_ int sd_lldp_attach_event(sd_lldp *lldp, sd_event *event, int64_t priority) {
+ int r;
- lldp_neighbour_port_remove_and_free(p);
+ assert_return(lldp, -EINVAL);
+ assert_return(lldp->fd < 0, -EBUSY);
+ assert_return(!lldp->event, -EBUSY);
- lldp->statistics.stats_ageouts_total ++;
+ if (event)
+ lldp->event = sd_event_ref(event);
+ else {
+ r = sd_event_default(&lldp->event);
+ if (r < 0)
+ return r;
}
-}
-static void lldp_mib_objects_flush(sd_lldp *lldp) {
- lldp_neighbour_port *p, *q;
- lldp_chassis *c;
+ lldp->event_priority = priority;
- assert(lldp);
- assert(lldp->neighbour_mib);
- assert(lldp->by_expiry);
+ return 0;
+}
- /* Drop all packets */
- while ((c = hashmap_steal_first(lldp->neighbour_mib))) {
+_public_ int sd_lldp_detach_event(sd_lldp *lldp) {
- LIST_FOREACH_SAFE(port, p, q, c->ports) {
- lldp_neighbour_port_remove_and_free(p);
- }
- }
+ assert_return(lldp, -EINVAL);
+ assert_return(lldp->fd < 0, -EBUSY);
- assert(hashmap_size(lldp->neighbour_mib) == 0);
- assert(prioq_size(lldp->by_expiry) == 0);
+ lldp->event = sd_event_unref(lldp->event);
+ return 0;
}
-int sd_lldp_save(sd_lldp *lldp, const char *lldp_file) {
- _cleanup_free_ char *temp_path = NULL;
- _cleanup_fclose_ FILE *f = NULL;
- uint8_t *mac, *port_id, type;
- lldp_neighbour_port *p;
- uint16_t data = 0, length = 0;
- char buf[LINE_MAX];
- lldp_chassis *c;
- usec_t time;
- Iterator i;
- int r;
+_public_ sd_event* sd_lldp_get_event(sd_lldp *lldp) {
+ assert_return(lldp, NULL);
- assert(lldp);
- assert(lldp_file);
+ return lldp->event;
+}
- r = fopen_temporary(lldp_file, &f, &temp_path);
- if (r < 0)
- goto finish;
+_public_ int sd_lldp_set_callback(sd_lldp *lldp, sd_lldp_callback_t cb, void *userdata) {
+ assert_return(lldp, -EINVAL);
- fchmod(fileno(f), 0644);
+ lldp->callback = cb;
+ lldp->userdata = userdata;
- HASHMAP_FOREACH(c, lldp->neighbour_mib, i) {
- LIST_FOREACH(port, p, c->ports) {
- _cleanup_free_ char *s = NULL;
- char *k, *t;
+ return 0;
+}
- r = lldp_read_chassis_id(p->packet, &type, &length, &mac);
- if (r < 0)
- continue;
+_public_ int sd_lldp_set_ifindex(sd_lldp *lldp, int ifindex) {
+ assert_return(lldp, -EINVAL);
+ assert_return(ifindex > 0, -EINVAL);
+ assert_return(lldp->fd < 0, -EBUSY);
- sprintf(buf, "'_Chassis=%02x:%02x:%02x:%02x:%02x:%02x' '_CType=%d' ",
- mac[0], mac[1], mac[2], mac[3], mac[4], mac[5], type);
+ lldp->ifindex = ifindex;
+ return 0;
+}
- s = strdup(buf);
- if (!s)
- return -ENOMEM;
+_public_ sd_lldp* sd_lldp_ref(sd_lldp *lldp) {
- r = lldp_read_port_id(p->packet, &type, &length, &port_id);
- if (r < 0)
- continue;
+ if (!lldp)
+ return NULL;
- if (type != LLDP_PORT_SUBTYPE_MAC_ADDRESS) {
- k = strndup((char *) port_id, length -1);
- if (!k)
- return -ENOMEM;
+ assert(lldp->n_ref > 0);
+ lldp->n_ref++;
- sprintf(buf, "'_Port=%s' '_PType=%d' ", k , type);
- free(k);
- } else {
- mac = port_id;
- sprintf(buf, "'_Port=%02x:%02x:%02x:%02x:%02x:%02x' '_PType=%d' ",
- mac[0], mac[1], mac[2], mac[3], mac[4], mac[5], type);
- }
+ return lldp;
+}
- k = strappend(s, buf);
- if (!k)
- return -ENOMEM;
+_public_ sd_lldp* sd_lldp_unref(sd_lldp *lldp) {
- free(s);
- s = k;
+ if (!lldp)
+ return NULL;
- time = now(CLOCK_BOOTTIME);
+ assert(lldp->n_ref > 0);
+ lldp->n_ref --;
- /* Don't write expired packets */
- if (time - p->until <= 0)
- continue;
+ if (lldp->n_ref > 0)
+ return NULL;
- sprintf(buf, "'_TTL="USEC_FMT"' ", p->until);
+ lldp_reset(lldp);
+ sd_lldp_detach_event(lldp);
+ lldp_flush_neighbors(lldp);
- k = strappend(s, buf);
- if (!k)
- return -ENOMEM;
+ hashmap_free(lldp->neighbor_by_id);
+ prioq_free(lldp->neighbor_by_expiry);
+ free(lldp);
- free(s);
- s = k;
+ return NULL;
+}
- r = lldp_read_system_name(p->packet, &length, &k);
- if (r < 0)
- k = strappend(s, "'_NAME=N/A' ");
- else {
- t = strndup(k, length);
- if (!t)
- return -ENOMEM;
+_public_ int sd_lldp_new(sd_lldp **ret) {
+ _cleanup_(sd_lldp_unrefp) sd_lldp *lldp = NULL;
+ int r;
- k = strjoin(s, "'_NAME=", t, "' ", NULL);
- free(t);
- }
+ assert_return(ret, -EINVAL);
- if (!k)
- return -ENOMEM;
+ lldp = new0(sd_lldp, 1);
+ if (!lldp)
+ return -ENOMEM;
- free(s);
- s = k;
+ lldp->n_ref = 1;
+ lldp->fd = -1;
+ lldp->neighbors_max = LLDP_DEFAULT_NEIGHBORS_MAX;
+ lldp->capability_mask = (uint16_t) -1;
- (void) lldp_read_system_capability(p->packet, &data);
+ lldp->neighbor_by_id = hashmap_new(&lldp_neighbor_id_hash_ops);
+ if (!lldp->neighbor_by_id)
+ return -ENOMEM;
- sprintf(buf, "'_CAP=%x'", data);
+ r = prioq_ensure_allocated(&lldp->neighbor_by_expiry, lldp_neighbor_prioq_compare_func);
+ if (r < 0)
+ return r;
- k = strappend(s, buf);
- if (!k)
- return -ENOMEM;
+ *ret = lldp;
+ lldp = NULL;
- free(s);
- s = k;
+ return 0;
+}
- fprintf(f, "%s\n", s);
- }
- }
- r = 0;
+static int neighbor_compare_func(const void *a, const void *b) {
+ const sd_lldp_neighbor * const*x = a, * const *y = b;
- fflush(f);
+ return lldp_neighbor_id_hash_ops.compare(&(*x)->id, &(*y)->id);
+}
- if (ferror(f) || rename(temp_path, lldp_file) < 0) {
- r = -errno;
- unlink(lldp_file);
- unlink(temp_path);
- }
+static int on_timer_event(sd_event_source *s, uint64_t usec, void *userdata) {
+ sd_lldp *lldp = userdata;
+ int r, q;
- finish:
+ r = lldp_make_space(lldp, 0);
if (r < 0)
- log_error("Failed to save lldp data %s: %s", lldp_file, strerror(-r));
+ return log_lldp_errno(r, "Failed to make space: %m");
- return r;
+ q = lldp_start_timer(lldp, NULL);
+ if (q < 0)
+ return log_lldp_errno(q, "Failed to restart timer: %m");
+
+ return 0;
}
-int sd_lldp_start(sd_lldp *lldp) {
+static int lldp_start_timer(sd_lldp *lldp, sd_lldp_neighbor *neighbor) {
+ sd_lldp_neighbor *n;
int r;
- assert_return(lldp, -EINVAL);
- assert_return(lldp->port, -EINVAL);
-
- lldp->port->status = LLDP_PORT_STATUS_ENABLED;
+ assert(lldp);
- lldp_set_state(lldp, LLDP_AGENT_RX_LLDP_INITIALIZE);
+ if (neighbor)
+ lldp_neighbor_start_ttl(neighbor);
- r = lldp_port_start(lldp->port);
- if (r < 0) {
- log_lldp("Failed to start Port : %s , %s",
- lldp->port->ifname,
- strerror(-r));
+ n = prioq_peek(lldp->neighbor_by_expiry);
+ if (!n) {
- lldp_set_state(lldp, LLDP_AGENT_RX_WAIT_PORT_OPERATIONAL);
+ if (lldp->timer_event_source)
+ return sd_event_source_set_enabled(lldp->timer_event_source, SD_EVENT_OFF);
- return r;
+ return 0;
}
- lldp_set_state(lldp, LLDP_AGENT_RX_WAIT_FOR_FRAME);
-
- return 0;
-}
-
-int sd_lldp_stop(sd_lldp *lldp) {
- int r;
+ if (lldp->timer_event_source) {
+ r = sd_event_source_set_time(lldp->timer_event_source, n->until);
+ if (r < 0)
+ return r;
- assert_return(lldp, -EINVAL);
- assert_return(lldp->port, -EINVAL);
+ return sd_event_source_set_enabled(lldp->timer_event_source, SD_EVENT_ONESHOT);
+ }
- lldp->port->status = LLDP_PORT_STATUS_DISABLED;
+ if (!lldp->event)
+ return 0;
- r = lldp_port_stop(lldp->port);
+ r = sd_event_add_time(lldp->event, &lldp->timer_event_source, clock_boottime_or_monotonic(), n->until, 0, on_timer_event, lldp);
if (r < 0)
return r;
- lldp_mib_objects_flush(lldp);
+ r = sd_event_source_set_priority(lldp->timer_event_source, lldp->event_priority);
+ if (r < 0)
+ return r;
+ (void) sd_event_source_set_description(lldp->timer_event_source, "lldp-timer");
return 0;
}
-int sd_lldp_attach_event(sd_lldp *lldp, sd_event *event, int priority) {
- int r;
+_public_ int sd_lldp_get_neighbors(sd_lldp *lldp, sd_lldp_neighbor ***ret) {
+ sd_lldp_neighbor **l = NULL, *n;
+ Iterator i;
+ int k = 0, r;
assert_return(lldp, -EINVAL);
- assert_return(!lldp->port->event, -EBUSY);
+ assert_return(ret, -EINVAL);
- if (event)
- lldp->port->event = sd_event_ref(event);
- else {
- r = sd_event_default(&lldp->port->event);
- if (r < 0)
- return r;
+ if (hashmap_isempty(lldp->neighbor_by_id)) { /* Special shortcut */
+ *ret = NULL;
+ return 0;
}
- lldp->port->event_priority = priority;
+ l = new0(sd_lldp_neighbor*, hashmap_size(lldp->neighbor_by_id));
+ if (!l)
+ return -ENOMEM;
- return 0;
-}
+ r = lldp_start_timer(lldp, NULL);
+ if (r < 0) {
+ free(l);
+ return r;
+ }
-int sd_lldp_detach_event(sd_lldp *lldp) {
+ HASHMAP_FOREACH(n, lldp->neighbor_by_id, i)
+ l[k++] = sd_lldp_neighbor_ref(n);
- assert_return(lldp, -EINVAL);
+ assert((size_t) k == hashmap_size(lldp->neighbor_by_id));
- lldp->port->event = sd_event_unref(lldp->port->event);
+ /* Return things in a stable order */
+ qsort(l, k, sizeof(sd_lldp_neighbor*), neighbor_compare_func);
+ *ret = l;
- return 0;
+ return k;
}
-int sd_lldp_set_callback(sd_lldp *lldp, sd_lldp_cb_t cb, void *userdata) {
+_public_ int sd_lldp_set_neighbors_max(sd_lldp *lldp, uint64_t m) {
assert_return(lldp, -EINVAL);
+ assert_return(m <= 0, -EINVAL);
- lldp->cb = cb;
- lldp->userdata = userdata;
+ lldp->neighbors_max = m;
+ lldp_make_space(lldp, 0);
return 0;
}
-void sd_lldp_free(sd_lldp *lldp) {
-
- if (!lldp)
- return;
-
- /* Drop all packets */
- lldp_mib_objects_flush(lldp);
-
- lldp_port_free(lldp->port);
+_public_ int sd_lldp_match_capabilities(sd_lldp *lldp, uint16_t mask) {
+ assert_return(lldp, -EINVAL);
+ assert_return(mask != 0, -EINVAL);
- hashmap_free(lldp->neighbour_mib);
- prioq_free(lldp->by_expiry);
+ lldp->capability_mask = mask;
- free(lldp);
+ return 0;
}
-int sd_lldp_new(int ifindex,
- const char *ifname,
- const struct ether_addr *mac,
- sd_lldp **ret) {
- _cleanup_lldp_free_ sd_lldp *lldp = NULL;
- int r;
-
- assert_return(ret, -EINVAL);
- assert_return(ifindex > 0, -EINVAL);
- assert_return(ifname, -EINVAL);
- assert_return(mac, -EINVAL);
-
- lldp = new0(sd_lldp, 1);
- if (!lldp)
- return -ENOMEM;
-
- r = lldp_port_new(ifindex, ifname, mac, lldp, &lldp->port);
- if (r < 0)
- return r;
-
- lldp->neighbour_mib = hashmap_new(&chassis_id_hash_ops);
- if (!lldp->neighbour_mib)
- return -ENOMEM;
-
- r = prioq_ensure_allocated(&lldp->by_expiry,
- ttl_expiry_item_prioq_compare_func);
- if (r < 0)
- return r;
+_public_ int sd_lldp_set_filter_address(sd_lldp *lldp, const struct ether_addr *addr) {
+ assert_return(lldp, -EINVAL);
- lldp->rx_state = LLDP_AGENT_RX_WAIT_PORT_OPERATIONAL;
+ /* In order to deal nicely with bridges that send back our own packets, allow one address to be filtered, so
+ * that our own can be filtered out here. */
- *ret = lldp;
- lldp = NULL;
+ if (addr)
+ lldp->filter_address = *addr;
+ else
+ zero(lldp->filter_address);
return 0;
}
diff --git a/src/libsystemd-network/sd-ndisc.c b/src/libsystemd-network/sd-ndisc.c
new file mode 100644
index 0000000000..07b0d7f704
--- /dev/null
+++ b/src/libsystemd-network/sd-ndisc.c
@@ -0,0 +1,422 @@
+/***
+ This file is part of systemd.
+
+ Copyright (C) 2014 Intel Corporation. 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/icmp6.h>
+#include <netinet/in.h>
+
+#include "sd-ndisc.h"
+
+#include "alloc-util.h"
+#include "fd-util.h"
+#include "icmp6-util.h"
+#include "in-addr-util.h"
+#include "ndisc-internal.h"
+#include "ndisc-router.h"
+#include "socket-util.h"
+#include "string-util.h"
+#include "util.h"
+
+#define NDISC_ROUTER_SOLICITATION_INTERVAL (4U * USEC_PER_SEC)
+#define NDISC_MAX_ROUTER_SOLICITATIONS 3U
+
+static void ndisc_callback(sd_ndisc *ndisc, sd_ndisc_event event, sd_ndisc_router *rt) {
+ assert(ndisc);
+
+ log_ndisc("Invoking callback for '%c'.", event);
+
+ if (!ndisc->callback)
+ return;
+
+ ndisc->callback(ndisc, event, rt, ndisc->userdata);
+}
+
+_public_ int sd_ndisc_set_callback(
+ sd_ndisc *nd,
+ sd_ndisc_callback_t callback,
+ void *userdata) {
+
+ assert_return(nd, -EINVAL);
+
+ nd->callback = callback;
+ nd->userdata = userdata;
+
+ return 0;
+}
+
+_public_ int sd_ndisc_set_ifindex(sd_ndisc *nd, int ifindex) {
+ assert_return(nd, -EINVAL);
+ assert_return(ifindex > 0, -EINVAL);
+ assert_return(nd->fd < 0, -EBUSY);
+
+ nd->ifindex = ifindex;
+ return 0;
+}
+
+_public_ int sd_ndisc_set_mac(sd_ndisc *nd, const struct ether_addr *mac_addr) {
+ assert_return(nd, -EINVAL);
+
+ if (mac_addr)
+ nd->mac_addr = *mac_addr;
+ else
+ zero(nd->mac_addr);
+
+ return 0;
+}
+
+_public_ int sd_ndisc_attach_event(sd_ndisc *nd, sd_event *event, int64_t priority) {
+ int r;
+
+ assert_return(nd, -EINVAL);
+ assert_return(nd->fd < 0, -EBUSY);
+ assert_return(!nd->event, -EBUSY);
+
+ if (event)
+ nd->event = sd_event_ref(event);
+ else {
+ r = sd_event_default(&nd->event);
+ if (r < 0)
+ return 0;
+ }
+
+ nd->event_priority = priority;
+
+ return 0;
+}
+
+_public_ int sd_ndisc_detach_event(sd_ndisc *nd) {
+
+ assert_return(nd, -EINVAL);
+ assert_return(nd->fd < 0, -EBUSY);
+
+ nd->event = sd_event_unref(nd->event);
+ return 0;
+}
+
+_public_ sd_event *sd_ndisc_get_event(sd_ndisc *nd) {
+ assert_return(nd, NULL);
+
+ return nd->event;
+}
+
+_public_ sd_ndisc *sd_ndisc_ref(sd_ndisc *nd) {
+
+ if (!nd)
+ return NULL;
+
+ assert(nd->n_ref > 0);
+ nd->n_ref++;
+
+ return nd;
+}
+
+static int ndisc_reset(sd_ndisc *nd) {
+ assert(nd);
+
+ nd->timeout_event_source = sd_event_source_unref(nd->timeout_event_source);
+ nd->recv_event_source = sd_event_source_unref(nd->recv_event_source);
+ nd->fd = safe_close(nd->fd);
+
+ return 0;
+}
+
+_public_ sd_ndisc *sd_ndisc_unref(sd_ndisc *nd) {
+
+ if (!nd)
+ return NULL;
+
+ assert(nd->n_ref > 0);
+ nd->n_ref--;
+
+ if (nd->n_ref > 0)
+ return NULL;
+
+ ndisc_reset(nd);
+ sd_ndisc_detach_event(nd);
+ free(nd);
+
+ return NULL;
+}
+
+_public_ int sd_ndisc_new(sd_ndisc **ret) {
+ _cleanup_(sd_ndisc_unrefp) sd_ndisc *nd = NULL;
+
+ assert_return(ret, -EINVAL);
+
+ nd = new0(sd_ndisc, 1);
+ if (!nd)
+ return -ENOMEM;
+
+ nd->n_ref = 1;
+ nd->fd = -1;
+
+ *ret = nd;
+ nd = NULL;
+
+ return 0;
+}
+
+_public_ int sd_ndisc_get_mtu(sd_ndisc *nd, uint32_t *mtu) {
+ assert_return(nd, -EINVAL);
+ assert_return(mtu, -EINVAL);
+
+ if (nd->mtu == 0)
+ return -ENODATA;
+
+ *mtu = nd->mtu;
+ return 0;
+}
+
+_public_ int sd_ndisc_get_hop_limit(sd_ndisc *nd, uint8_t *ret) {
+ assert_return(nd, -EINVAL);
+ assert_return(ret, -EINVAL);
+
+ if (nd->hop_limit == 0)
+ return -ENODATA;
+
+ *ret = nd->hop_limit;
+ return 0;
+}
+
+static int ndisc_handle_datagram(sd_ndisc *nd, sd_ndisc_router *rt) {
+ int r;
+
+ assert(nd);
+ assert(rt);
+
+ r = ndisc_router_parse(rt);
+ if (r == -EBADMSG) /* Bad packet */
+ return 0;
+ if (r < 0)
+ return 0;
+
+ /* Update global variables we keep */
+ if (rt->mtu > 0)
+ nd->mtu = rt->mtu;
+ if (rt->hop_limit > 0)
+ nd->hop_limit = rt->hop_limit;
+
+ log_ndisc("Received Router Advertisement: flags %s preference %s lifetime %" PRIu16 " sec",
+ rt->flags & ND_RA_FLAG_MANAGED ? "MANAGED" : rt->flags & ND_RA_FLAG_OTHER ? "OTHER" : "none",
+ rt->preference == SD_NDISC_PREFERENCE_HIGH ? "high" : rt->preference == SD_NDISC_PREFERENCE_LOW ? "low" : "medium",
+ rt->lifetime);
+
+ ndisc_callback(nd, SD_NDISC_EVENT_ROUTER, rt);
+ return 0;
+}
+
+static int ndisc_recv(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
+ _cleanup_(sd_ndisc_router_unrefp) sd_ndisc_router *rt = NULL;
+ sd_ndisc *nd = userdata;
+ union {
+ struct cmsghdr cmsghdr;
+ uint8_t buf[CMSG_SPACE(sizeof(int)) + /* ttl */
+ CMSG_SPACE(sizeof(struct timeval))];
+ } control = {};
+ struct iovec iov = {};
+ union sockaddr_union sa = {};
+ struct msghdr msg = {
+ .msg_name = &sa.sa,
+ .msg_namelen = sizeof(sa),
+ .msg_iov = &iov,
+ .msg_iovlen = 1,
+ .msg_control = &control,
+ .msg_controllen = sizeof(control),
+ };
+ struct cmsghdr *cmsg;
+ ssize_t len, buflen;
+
+ assert(s);
+ assert(nd);
+ assert(nd->event);
+
+ buflen = next_datagram_size_fd(fd);
+ if (buflen < 0)
+ return log_ndisc_errno(buflen, "Failed to determine datagram size to read: %m");
+
+ rt = ndisc_router_new(buflen);
+ if (!rt)
+ return -ENOMEM;
+
+ iov.iov_base = NDISC_ROUTER_RAW(rt);
+ iov.iov_len = rt->raw_size;
+
+ len = recvmsg(fd, &msg, MSG_DONTWAIT);
+ if (len < 0) {
+ if (errno == EAGAIN || errno == EINTR)
+ return 0;
+
+ return log_ndisc_errno(errno, "Could not receive message from ICMPv6 socket: %m");
+ }
+
+ if ((size_t) len != rt->raw_size) {
+ log_ndisc("Packet size mismatch.");
+ return -EINVAL;
+ }
+
+ if (msg.msg_namelen == sizeof(struct sockaddr_in6) &&
+ sa.in6.sin6_family == AF_INET6) {
+
+ if (in_addr_is_link_local(AF_INET6, (union in_addr_union*) &sa.in6.sin6_addr) <= 0) {
+ _cleanup_free_ char *addr = NULL;
+
+ (void) in_addr_to_string(AF_INET6, (union in_addr_union*) &sa.in6.sin6_addr, &addr);
+ log_ndisc("Received RA from non-link-local address %s. Ignoring.", strna(addr));
+ return 0;
+ }
+
+ rt->address = sa.in6.sin6_addr;
+
+ } else if (msg.msg_namelen > 0) {
+ log_ndisc("Received invalid source address size from ICMPv6 socket: %zu bytes", (size_t) msg.msg_namelen);
+ return -EINVAL;
+ }
+
+ /* namelen == 0 only happens when running the test-suite over a socketpair */
+
+ assert(!(msg.msg_flags & MSG_CTRUNC));
+ assert(!(msg.msg_flags & MSG_TRUNC));
+
+ CMSG_FOREACH(cmsg, &msg) {
+ if (cmsg->cmsg_level == SOL_IPV6 &&
+ cmsg->cmsg_type == IPV6_HOPLIMIT &&
+ cmsg->cmsg_len == CMSG_LEN(sizeof(int))) {
+ int hops = *(int*) CMSG_DATA(cmsg);
+
+ if (hops != 255) {
+ log_ndisc("Received RA with invalid hop limit %d. Ignoring.", hops);
+ return 0;
+ }
+ }
+
+ if (cmsg->cmsg_level == SOL_SOCKET &&
+ cmsg->cmsg_type == SO_TIMESTAMP &&
+ cmsg->cmsg_len == CMSG_LEN(sizeof(struct timeval)))
+ triple_timestamp_from_realtime(&rt->timestamp, timeval_load((struct timeval*) CMSG_DATA(cmsg)));
+ }
+
+ if (!triple_timestamp_is_set(&rt->timestamp))
+ triple_timestamp_get(&rt->timestamp);
+
+ nd->timeout_event_source = sd_event_source_unref(nd->timeout_event_source);
+
+ return ndisc_handle_datagram(nd, rt);
+}
+
+static int ndisc_timeout(sd_event_source *s, uint64_t usec, void *userdata) {
+ sd_ndisc *nd = userdata;
+ usec_t time_now, next_timeout;
+ int r;
+
+ assert(s);
+ assert(nd);
+ assert(nd->event);
+
+ if (nd->nd_sent >= NDISC_MAX_ROUTER_SOLICITATIONS) {
+ nd->timeout_event_source = sd_event_source_unref(nd->timeout_event_source);
+ ndisc_callback(nd, SD_NDISC_EVENT_TIMEOUT, NULL);
+ return 0;
+ }
+
+ r = icmp6_send_router_solicitation(nd->fd, &nd->mac_addr);
+ if (r < 0) {
+ log_ndisc_errno(r, "Error sending Router Solicitation: %m");
+ goto fail;
+ }
+
+ log_ndisc("Sent Router Solicitation");
+ nd->nd_sent++;
+
+ assert_se(sd_event_now(nd->event, clock_boottime_or_monotonic(), &time_now) >= 0);
+ next_timeout = time_now + NDISC_ROUTER_SOLICITATION_INTERVAL;
+
+ r = sd_event_source_set_time(nd->timeout_event_source, next_timeout);
+ if (r < 0) {
+ log_ndisc_errno(r, "Error updating timer: %m");
+ goto fail;
+ }
+
+ r = sd_event_source_set_enabled(nd->timeout_event_source, SD_EVENT_ONESHOT);
+ if (r < 0) {
+ log_ndisc_errno(r, "Error reenabling timer: %m");
+ goto fail;
+ }
+
+ return 0;
+
+fail:
+ sd_ndisc_stop(nd);
+ return 0;
+}
+
+_public_ int sd_ndisc_stop(sd_ndisc *nd) {
+ assert_return(nd, -EINVAL);
+
+ if (nd->fd < 0)
+ return 0;
+
+ log_ndisc("Stopping IPv6 Router Solicitation client");
+
+ ndisc_reset(nd);
+ return 1;
+}
+
+_public_ int sd_ndisc_start(sd_ndisc *nd) {
+ int r;
+
+ assert_return(nd, -EINVAL);
+ assert_return(nd->event, -EINVAL);
+ assert_return(nd->ifindex > 0, -EINVAL);
+
+ if (nd->fd >= 0)
+ return 0;
+
+ assert(!nd->recv_event_source);
+ assert(!nd->timeout_event_source);
+
+ nd->fd = icmp6_bind_router_solicitation(nd->ifindex);
+ if (nd->fd < 0)
+ return nd->fd;
+
+ r = sd_event_add_io(nd->event, &nd->recv_event_source, nd->fd, EPOLLIN, ndisc_recv, nd);
+ if (r < 0)
+ goto fail;
+
+ r = sd_event_source_set_priority(nd->recv_event_source, nd->event_priority);
+ if (r < 0)
+ goto fail;
+
+ (void) sd_event_source_set_description(nd->recv_event_source, "ndisc-receive-message");
+
+ r = sd_event_add_time(nd->event, &nd->timeout_event_source, clock_boottime_or_monotonic(), 0, 0, ndisc_timeout, nd);
+ if (r < 0)
+ goto fail;
+
+ r = sd_event_source_set_priority(nd->timeout_event_source, nd->event_priority);
+ if (r < 0)
+ goto fail;
+
+ (void) sd_event_source_set_description(nd->timeout_event_source, "ndisc-timeout");
+
+ log_ndisc("Started IPv6 Router Solicitation client");
+ return 1;
+
+fail:
+ ndisc_reset(nd);
+ return r;
+}
diff --git a/src/libsystemd-network/sd-pppoe.c b/src/libsystemd-network/sd-pppoe.c
deleted file mode 100644
index 1de8a5e8bf..0000000000
--- a/src/libsystemd-network/sd-pppoe.c
+++ /dev/null
@@ -1,802 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- This file is part of systemd.
-
- Copyright (C) 2014 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/>.
-***/
-
-/* See RFC 2516 */
-
-#include <sys/ioctl.h>
-#include <linux/ppp_defs.h>
-#include <linux/ppp-ioctl.h>
-#include <net/if.h>
-#include <netinet/in.h>
-#include <linux/if_pppox.h>
-
-#include "sd-pppoe.h"
-
-#include "event-util.h"
-
-#include "util.h"
-#include "random-util.h"
-#include "socket-util.h"
-#include "async.h"
-#include "refcnt.h"
-#include "utf8.h"
-
-#define PPPOE_MAX_PACKET_SIZE 1484
-#define PPPOE_MAX_PADR_RESEND 16
-
-/* TODO: move this to socket-util.h without getting into
- * a mess with the includes */
-union sockaddr_union_pppox {
- struct sockaddr sa;
- struct sockaddr_pppox pppox;
-};
-
-typedef enum PPPoEState {
- PPPOE_STATE_INITIALIZING,
- PPPOE_STATE_REQUESTING,
- PPPOE_STATE_RUNNING,
- PPPOE_STATE_STOPPED,
- _PPPOE_STATE_MAX,
- _PPPOE_STATE_INVALID = -1,
-} PPPoEState;
-
-typedef struct PPPoETags {
- char *service_name;
- char *ac_name;
- uint8_t *host_uniq;
- size_t host_uniq_len;
- uint8_t *cookie;
- size_t cookie_len;
-} PPPoETags;
-
-struct sd_pppoe {
- RefCount n_ref;
-
- PPPoEState state;
- uint64_t host_uniq;
-
- int ifindex;
- char *ifname;
-
- sd_event *event;
- int event_priority;
- int fd;
- sd_event_source *io;
- sd_event_source *timeout;
- int padr_resend_count;
-
- char *service_name;
- struct ether_addr peer_mac;
- be16_t session_id;
-
- int pppoe_fd;
- int channel;
-
- sd_pppoe_cb_t cb;
- void *userdata;
-
- PPPoETags tags;
-};
-
-#define PPPOE_PACKET_LENGTH(header) \
- be16toh((header)->length)
-
-#define PPPOE_PACKET_TAIL(packet) \
- (struct pppoe_tag*)((uint8_t*)(packet) + sizeof(struct pppoe_hdr) + PPPOE_PACKET_LENGTH(packet))
-
-#define PPPOE_TAG_LENGTH(tag) \
- be16toh((tag)->tag_len)
-
-#define PPPOE_TAG_TYPE(tag) \
- (tag)->tag_type
-
-#define PPPOE_TAG_NEXT(tag) \
- (struct pppoe_tag *)((uint8_t *)(tag) + sizeof(struct pppoe_tag) + PPPOE_TAG_LENGTH(tag))
-
-#define PPPOE_TAGS_FOREACH(tag, header) \
- for (tag = (header)->tag; \
- ((uint8_t *)(tag) + sizeof(struct pppoe_tag) < (uint8_t*)PPPOE_PACKET_TAIL(header)) && \
- (PPPOE_TAG_NEXT(tag) <= PPPOE_PACKET_TAIL(header)) && \
- (tag >= (header)->tag) && \
- (PPPOE_TAG_TYPE(tag) != PTT_EOL); \
- tag = PPPOE_TAG_NEXT(tag))
-
-static void pppoe_tags_clear(PPPoETags *tags) {
- free(tags->service_name);
- free(tags->ac_name);
- free(tags->host_uniq);
- free(tags->cookie);
-
- zero(*tags);
-}
-
-int sd_pppoe_set_ifindex(sd_pppoe *ppp, int ifindex) {
- assert_return(ppp, -EINVAL);
- assert_return(ifindex > 0, -EINVAL);
-
- ppp->ifindex = ifindex;
-
- return 0;
-}
-
-int sd_pppoe_set_ifname(sd_pppoe *ppp, const char *ifname) {
- char *name;
-
- assert_return(ppp, -EINVAL);
- assert_return(ifname, -EINVAL);
-
- if (strlen(ifname) > IFNAMSIZ)
- return -EINVAL;
-
- name = strdup(ifname);
- if (!name)
- return -ENOMEM;
-
- free(ppp->ifname);
- ppp->ifname = name;
-
- return 0;
-}
-
-int sd_pppoe_set_service_name(sd_pppoe *ppp, const char *service_name) {
- _cleanup_free_ char *name = NULL;
-
- assert_return(ppp, -EINVAL);
-
- if (service_name) {
- name = strdup(service_name);
- if (!name)
- return -ENOMEM;
- }
-
- free(ppp->service_name);
- ppp->service_name = name;
- name = NULL;
-
- return 0;
-}
-
-int sd_pppoe_attach_event(sd_pppoe *ppp, sd_event *event, int priority) {
- int r;
-
- assert_return(ppp, -EINVAL);
- assert_return(!ppp->event, -EBUSY);
-
- if (event)
- ppp->event = sd_event_ref(event);
- else {
- r = sd_event_default(&ppp->event);
- if (r < 0)
- return r;
- }
-
- ppp->event_priority = priority;
-
- return 0;
-}
-
-int sd_pppoe_detach_event(sd_pppoe *ppp) {
- assert_return(ppp, -EINVAL);
-
- ppp->event = sd_event_unref(ppp->event);
-
- return 0;
-}
-
-sd_pppoe *sd_pppoe_ref(sd_pppoe *ppp) {
- if (ppp)
- assert_se(REFCNT_INC(ppp->n_ref) >= 2);
-
- return ppp;
-}
-
-sd_pppoe *sd_pppoe_unref(sd_pppoe *ppp) {
- if (ppp && REFCNT_DEC(ppp->n_ref) <= 0) {
- pppoe_tags_clear(&ppp->tags);
- free(ppp->ifname);
- free(ppp->service_name);
- sd_pppoe_stop(ppp);
- sd_pppoe_detach_event(ppp);
-
- free(ppp);
- }
-
- return NULL;
-}
-
-int sd_pppoe_new (sd_pppoe **ret) {
- sd_pppoe *ppp;
-
- assert_return(ret, -EINVAL);
-
- ppp = new0(sd_pppoe, 1);
- if (!ppp)
- return -ENOMEM;
-
- ppp->n_ref = REFCNT_INIT;
- ppp->state = _PPPOE_STATE_INVALID;
- ppp->ifindex = -1;
- ppp->fd = -1;
- ppp->pppoe_fd = -1;
- ppp->padr_resend_count = PPPOE_MAX_PADR_RESEND;
-
- *ret = ppp;
-
- return 0;
-}
-
-int sd_pppoe_get_channel(sd_pppoe *ppp, int *channel) {
- assert_return(ppp, -EINVAL);
- assert_return(channel, -EINVAL);
- assert_return(ppp->pppoe_fd != -1, -EUNATCH);
- assert_return(ppp->state == PPPOE_STATE_RUNNING, -EUNATCH);
-
- *channel = ppp->channel;
-
- return 0;
-}
-
-int sd_pppoe_set_callback(sd_pppoe *ppp, sd_pppoe_cb_t cb, void *userdata) {
- assert_return(ppp, -EINVAL);
-
- ppp->cb = cb;
- ppp->userdata = userdata;
-
- return 0;
-}
-
-static void pppoe_tag_append(struct pppoe_hdr *packet, size_t packet_size, be16_t tag_type, const void *tag_data, uint16_t tag_len) {
- struct pppoe_tag *tag;
-
- assert(packet);
- assert(sizeof(struct pppoe_hdr) + PPPOE_PACKET_LENGTH(packet) + sizeof(struct pppoe_tag) + tag_len <= packet_size);
- assert(!(!tag_data ^ !tag_len));
-
- tag = PPPOE_PACKET_TAIL(packet);
-
- tag->tag_len = htobe16(tag_len);
- tag->tag_type = tag_type;
- if (tag_data)
- memcpy(tag->tag_data, tag_data, tag_len);
-
- packet->length = htobe16(PPPOE_PACKET_LENGTH(packet) + sizeof(struct pppoe_tag) + tag_len);
-}
-
-static int pppoe_send(sd_pppoe *ppp, uint8_t code) {
- union sockaddr_union link = {
- .ll = {
- .sll_family = AF_PACKET,
- .sll_protocol = htons(ETH_P_PPP_DISC),
- .sll_halen = ETH_ALEN,
- },
- };
- _cleanup_free_ struct pppoe_hdr *packet = NULL;
- int r;
-
- assert(ppp);
- assert(ppp->fd != -1);
- assert(IN_SET(code, PADI_CODE, PADR_CODE, PADT_CODE));
-
- link.ll.sll_ifindex = ppp->ifindex;
- if (code == PADI_CODE)
- memset(&link.ll.sll_addr, 0xff, ETH_ALEN);
- else
- memcpy(&link.ll.sll_addr, &ppp->peer_mac, ETH_ALEN);
-
- packet = malloc0(PPPOE_MAX_PACKET_SIZE);
- if (!packet)
- return -ENOMEM;
-
- packet->ver = 0x1;
- packet->type = 0x1;
- packet->code = code;
- if (code == PADT_CODE)
- packet->sid = ppp->session_id;
-
- /* Service-Name */
- pppoe_tag_append(packet, PPPOE_MAX_PACKET_SIZE, PTT_SRV_NAME,
- ppp->service_name, ppp->service_name ? strlen(ppp->service_name) : 0);
-
- /* AC-Cookie */
- if (code == PADR_CODE && ppp->tags.cookie)
- pppoe_tag_append(packet, PPPOE_MAX_PACKET_SIZE, PTT_AC_COOKIE,
- ppp->tags.cookie, ppp->tags.cookie_len);
-
- /* Host-Uniq */
- if (code != PADT_CODE) {
- ppp->host_uniq = random_u64();
-
- pppoe_tag_append(packet, PPPOE_MAX_PACKET_SIZE, PTT_HOST_UNIQ,
- &ppp->host_uniq, sizeof(ppp->host_uniq));
- }
-
- r = sendto(ppp->fd, packet, sizeof(struct pppoe_hdr) + PPPOE_PACKET_LENGTH(packet),
- 0, &link.sa, sizeof(link.ll));
- if (r < 0)
- return -errno;
-
- return 0;
-}
-
-static int pppoe_timeout(sd_event_source *s, uint64_t usec, void *userdata);
-
-static int pppoe_arm_timeout(sd_pppoe *ppp) {
- _cleanup_event_source_unref_ sd_event_source *timeout = NULL;
- usec_t next_timeout = 0;
- int r;
-
- assert(ppp);
-
- r = sd_event_now(ppp->event, clock_boottime_or_monotonic(), &next_timeout);
- if (r == -ENODATA)
- next_timeout = now(clock_boottime_or_monotonic());
- else if (r < 0)
- return r;
-
- next_timeout += 500 * USEC_PER_MSEC;
-
- r = sd_event_add_time(ppp->event, &timeout, clock_boottime_or_monotonic(), next_timeout,
- 10 * USEC_PER_MSEC, pppoe_timeout, ppp);
- if (r < 0)
- return r;
-
- r = sd_event_source_set_priority(timeout, ppp->event_priority);
- if (r < 0)
- return r;
-
- sd_event_source_unref(ppp->timeout);
- ppp->timeout = timeout;
- timeout = NULL;
-
- return 0;
-}
-
-static int pppoe_send_initiation(sd_pppoe *ppp) {
- int r;
-
- r = pppoe_send(ppp, PADI_CODE);
- if (r < 0)
- return r;
-
- log_debug("PPPoE: sent DISCOVER (Service-Name: %s)",
- ppp->service_name ? : "");
-
- pppoe_arm_timeout(ppp);
-
- return r;
-}
-
-static int pppoe_send_request(sd_pppoe *ppp) {
- int r;
-
- r = pppoe_send(ppp, PADR_CODE);
- if (r < 0)
- return r;
-
- log_debug("PPPoE: sent REQUEST");
-
- ppp->padr_resend_count --;
-
- pppoe_arm_timeout(ppp);
-
- return 0;
-}
-
-static int pppoe_send_terminate(sd_pppoe *ppp) {
- int r;
-
- r = pppoe_send(ppp, PADT_CODE);
- if (r < 0)
- return r;
-
- log_debug("PPPoE: sent TERMINATE");
-
- return 0;
-}
-
-static int pppoe_timeout(sd_event_source *s, uint64_t usec, void *userdata) {
- sd_pppoe *ppp = userdata;
- int r;
-
- assert(ppp);
-
- switch (ppp->state) {
- case PPPOE_STATE_INITIALIZING:
- r = pppoe_send_initiation(ppp);
- if (r < 0)
- log_warning_errno(r, "PPPoE: sending PADI failed: %m");
-
- break;
- case PPPOE_STATE_REQUESTING:
- if (ppp->padr_resend_count <= 0) {
- log_debug("PPPoE: PADR timed out, restarting PADI");
-
- r = pppoe_send_initiation(ppp);
- if (r < 0)
- log_warning_errno(r, "PPPoE: sending PADI failed: %m");
-
- ppp->padr_resend_count = PPPOE_MAX_PADR_RESEND;
- ppp->state = PPPOE_STATE_INITIALIZING;
- } else {
- r = pppoe_send_request(ppp);
- if (r < 0)
- log_warning_errno(r, "PPPoE: sending PADR failed: %m");
- }
-
- break;
- default:
- assert_not_reached("timeout in invalid state");
- }
-
- return 0;
-}
-
-static int pppoe_tag_parse_binary(struct pppoe_tag *tag, uint8_t **ret, size_t *length) {
- uint8_t *data;
-
- assert(ret);
- assert(length);
-
- data = memdup(tag->tag_data, PPPOE_TAG_LENGTH(tag));
- if (!data)
- return -ENOMEM;
-
- free(*ret);
- *ret = data;
- *length = PPPOE_TAG_LENGTH(tag);
-
- return 0;
-}
-
-static int pppoe_tag_parse_string(struct pppoe_tag *tag, char **ret) {
- char *string;
-
- assert(ret);
-
- string = strndup(tag->tag_data, PPPOE_TAG_LENGTH(tag));
- if (!string)
- return -ENOMEM;
-
- free(*ret);
- *ret = string;
-
- return 0;
-}
-
-static int pppoe_payload_parse(PPPoETags *tags, struct pppoe_hdr *header) {
- struct pppoe_tag *tag;
- int r;
-
- assert(tags);
-
- pppoe_tags_clear(tags);
-
- PPPOE_TAGS_FOREACH(tag, header) {
- switch (PPPOE_TAG_TYPE(tag)) {
- case PTT_SRV_NAME:
- r = pppoe_tag_parse_string(tag, &tags->service_name);
- if (r < 0)
- return r;
-
- break;
- case PTT_AC_NAME:
- r = pppoe_tag_parse_string(tag, &tags->ac_name);
- if (r < 0)
- return r;
-
- break;
- case PTT_HOST_UNIQ:
- r = pppoe_tag_parse_binary(tag, &tags->host_uniq, &tags->host_uniq_len);
- if (r < 0)
- return r;
-
- break;
- case PTT_AC_COOKIE:
- r = pppoe_tag_parse_binary(tag, &tags->cookie, &tags->cookie_len);
- if (r < 0)
- return r;
-
- break;
- case PTT_SRV_ERR:
- case PTT_SYS_ERR:
- case PTT_GEN_ERR:
- {
- _cleanup_free_ char *error = NULL;
-
- /* TODO: do something more sensible with the error messages */
- r = pppoe_tag_parse_string(tag, &error);
- if (r < 0)
- return r;
-
- if (strlen(error) > 0 && utf8_is_valid(error))
- log_debug("PPPoE: error - '%s'", error);
- else
- log_debug("PPPoE: error");
-
- break;
- }
- default:
- log_debug("PPPoE: ignoring unknown PPPoE tag type: 0x%.2x", PPPOE_TAG_TYPE(tag));
- }
- }
-
- return 0;
-}
-
-static int pppoe_open_pppoe_socket(sd_pppoe *ppp) {
- int s;
-
- assert(ppp);
- assert(ppp->pppoe_fd == -1);
-
- s = socket(AF_PPPOX, SOCK_STREAM, 0);
- if (s < 0)
- return -errno;
-
- ppp->pppoe_fd = s;
-
- return 0;
-}
-
-static int pppoe_connect_pppoe_socket(sd_pppoe *ppp) {
- union sockaddr_union_pppox link = {
- .pppox = {
- .sa_family = AF_PPPOX,
- .sa_protocol = PX_PROTO_OE,
- },
- };
- int r, channel;
-
- assert(ppp);
- assert(ppp->pppoe_fd != -1);
- assert(ppp->session_id);
- assert(ppp->ifname);
-
- link.pppox.sa_addr.pppoe.sid = ppp->session_id;
- memcpy(link.pppox.sa_addr.pppoe.dev, ppp->ifname, strlen(ppp->ifname));
- memcpy(link.pppox.sa_addr.pppoe.remote, &ppp->peer_mac, ETH_ALEN);
-
- r = connect(ppp->pppoe_fd, &link.sa, sizeof(link.pppox));
- if (r < 0)
- return r;
-
- r = ioctl(ppp->pppoe_fd, PPPIOCGCHAN, &channel);
- if (r < 0)
- return -errno;
-
- ppp->channel = channel;
-
- return 0;
-}
-
-static int pppoe_handle_message(sd_pppoe *ppp, struct pppoe_hdr *packet, struct ether_addr *mac) {
- int r;
-
- assert(packet);
-
- if (packet->ver != 0x1 || packet->type != 0x1)
- return 0;
-
- r = pppoe_payload_parse(&ppp->tags, packet);
- if (r < 0)
- return 0;
-
- switch (ppp->state) {
- case PPPOE_STATE_INITIALIZING:
- if (packet->code != PADO_CODE)
- return 0;
-
- if (ppp->tags.host_uniq_len != sizeof(ppp->host_uniq) ||
- memcmp(ppp->tags.host_uniq, &ppp->host_uniq, sizeof(ppp->host_uniq)) != 0)
- return 0;
-
- log_debug("PPPoE: got OFFER (Peer: "
- "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx; "
- "Service-Name: '%s'; AC-Name: '%s')",
- mac->ether_addr_octet[0],
- mac->ether_addr_octet[1],
- mac->ether_addr_octet[2],
- mac->ether_addr_octet[3],
- mac->ether_addr_octet[4],
- mac->ether_addr_octet[5],
- ppp->tags.service_name ? : "",
- ppp->tags.ac_name ? : "");
-
- memcpy(&ppp->peer_mac, mac, ETH_ALEN);
-
- r = pppoe_open_pppoe_socket(ppp);
- if (r < 0) {
- log_warning("PPPoE: could not open socket");
- return r;
- }
-
- r = pppoe_send_request(ppp);
- if (r < 0)
- return 0;
-
- ppp->state = PPPOE_STATE_REQUESTING;
-
- break;
- case PPPOE_STATE_REQUESTING:
- if (packet->code != PADS_CODE)
- return 0;
-
- if (ppp->tags.host_uniq_len != sizeof(ppp->host_uniq) ||
- memcmp(ppp->tags.host_uniq, &ppp->host_uniq,
- sizeof(ppp->host_uniq)) != 0)
- return 0;
-
- if (memcmp(&ppp->peer_mac, mac, ETH_ALEN) != 0)
- return 0;
-
- ppp->session_id = packet->sid;
-
- log_debug("PPPoE: got CONFIRMATION (Session ID: %"PRIu16")",
- be16toh(ppp->session_id));
-
- r = pppoe_connect_pppoe_socket(ppp);
- if (r < 0) {
- log_warning("PPPoE: could not connect socket");
- return r;
- }
-
- ppp->state = PPPOE_STATE_RUNNING;
-
- ppp->timeout = sd_event_source_unref(ppp->timeout);
- assert(ppp->cb);
- ppp->cb(ppp, PPPOE_EVENT_RUNNING, ppp->userdata);
-
- break;
- case PPPOE_STATE_RUNNING:
- if (packet->code != PADT_CODE)
- return 0;
-
- if (memcmp(&ppp->peer_mac, mac, ETH_ALEN) != 0)
- return 0;
-
- if (ppp->session_id != packet->sid)
- return 0;
-
- log_debug("PPPoE: got TERMINATE");
-
- ppp->state = PPPOE_STATE_STOPPED;
-
- assert(ppp->cb);
- ppp->cb(ppp, PPPOE_EVENT_STOPPED, ppp->userdata);
-
- break;
- case PPPOE_STATE_STOPPED:
- break;
- default:
- assert_not_reached("PPPoE: invalid state when receiving message");
- }
-
- return 0;
-}
-
-static int pppoe_receive_message(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
- sd_pppoe *ppp = userdata;
- _cleanup_free_ struct pppoe_hdr *packet = NULL;
- union sockaddr_union link = {};
- socklen_t addrlen = sizeof(link);
- int buflen = 0, len, r;
-
- assert(ppp);
- assert(fd != -1);
-
- r = ioctl(fd, FIONREAD, &buflen);
- if (r < 0)
- return r;
-
- if (buflen < 0)
- /* this can't be right */
- return -EIO;
-
- packet = malloc0(buflen);
- if (!packet)
- return -ENOMEM;
-
- len = recvfrom(fd, packet, buflen, 0, &link.sa, &addrlen);
- if (len < 0) {
- log_warning_errno(r, "PPPoE: could not receive message from raw socket: %m");
- return 0;
- } else if ((size_t)len < sizeof(struct pppoe_hdr))
- return 0;
- else if ((size_t)len != sizeof(struct pppoe_hdr) + PPPOE_PACKET_LENGTH(packet))
- return 0;
-
- if (link.ll.sll_halen != ETH_ALEN)
- /* not ethernet? */
- return 0;
-
- r = pppoe_handle_message(ppp, packet, (struct ether_addr*)&link.ll.sll_addr);
- if (r < 0)
- return r;
-
- return 1;
-}
-
-int sd_pppoe_start(sd_pppoe *ppp) {
- union sockaddr_union link = {
- .ll = {
- .sll_family = AF_PACKET,
- .sll_protocol = htons(ETH_P_PPP_DISC),
- },
- };
- _cleanup_close_ int s = -1;
- _cleanup_event_source_unref_ sd_event_source *io = NULL;
- int r;
-
- assert_return(ppp, -EINVAL);
- assert_return(ppp->fd == -1, -EBUSY);
- assert_return(!ppp->io, -EBUSY);
- assert_return(ppp->ifindex > 0, -EUNATCH);
- assert_return(ppp->ifname, -EUNATCH);
- assert_return(ppp->event, -EUNATCH);
- assert_return(ppp->cb, -EUNATCH);
-
- s = socket(AF_PACKET, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0);
- if (s < 0)
- return -errno;
-
- link.ll.sll_ifindex = ppp->ifindex;
-
- r = bind(s, &link.sa, sizeof(link.ll));
- if (r < 0)
- return r;
-
- r = sd_event_add_io(ppp->event, &io,
- s, EPOLLIN, pppoe_receive_message,
- ppp);
- if (r < 0)
- return r;
-
- r = sd_event_source_set_priority(io, ppp->event_priority);
- if (r < 0)
- return r;
-
- ppp->fd = s;
- s = -1;
- ppp->io = io;
- io = NULL;
-
- r = pppoe_send_initiation(ppp);
- if (r < 0)
- return r;
-
- ppp->state = PPPOE_STATE_INITIALIZING;
-
- return 0;
-}
-
-int sd_pppoe_stop(sd_pppoe *ppp) {
- assert_return(ppp, -EINVAL);
-
- if (ppp->state == PPPOE_STATE_RUNNING)
- pppoe_send_terminate(ppp);
-
- ppp->io = sd_event_source_unref(ppp->io);
- ppp->timeout = sd_event_source_unref(ppp->timeout);
- ppp->fd = asynchronous_close(ppp->fd);
- ppp->pppoe_fd = asynchronous_close(ppp->pppoe_fd);
-
- return 0;
-}
diff --git a/src/libsystemd-network/test-acd.c b/src/libsystemd-network/test-acd.c
new file mode 100644
index 0000000000..27fcc332a3
--- /dev/null
+++ b/src/libsystemd-network/test-acd.c
@@ -0,0 +1,114 @@
+/***
+ 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 <errno.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <linux/veth.h>
+#include <net/if.h>
+
+#include "sd-event.h"
+#include "sd-ipv4acd.h"
+#include "sd-netlink.h"
+
+#include "in-addr-util.h"
+#include "netlink-util.h"
+#include "util.h"
+
+static void acd_handler(sd_ipv4acd *acd, int event, void *userdata) {
+ assert_se(acd);
+
+ switch (event) {
+ case SD_IPV4ACD_EVENT_BIND:
+ log_info("bound");
+ break;
+ case SD_IPV4ACD_EVENT_CONFLICT:
+ log_info("conflict");
+ break;
+ case SD_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_ifindex(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_(sd_event_unrefp) sd_event *e = NULL;
+ _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL;
+ _cleanup_(sd_netlink_message_unrefp) 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-dhcp-client.c b/src/libsystemd-network/test-dhcp-client.c
index d341210887..2a101cb1fe 100644
--- a/src/libsystemd-network/test-dhcp-client.c
+++ b/src/libsystemd-network/test-dhcp-client.c
@@ -1,5 +1,3 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
/***
This file is part of systemd.
@@ -24,14 +22,15 @@
#include <sys/socket.h>
#include <unistd.h>
-#include "util.h"
+#include "sd-dhcp-client.h"
#include "sd-event.h"
-#include "event-util.h"
+#include "alloc-util.h"
#include "dhcp-identifier.h"
-#include "dhcp-protocol.h"
#include "dhcp-internal.h"
-#include "sd-dhcp-client.h"
+#include "dhcp-protocol.h"
+#include "fd-util.h"
+#include "util.h"
static uint8_t mac_addr[] = {'A', 'B', 'C', '1', '2', '3'};
@@ -43,16 +42,13 @@ static test_callback_recv_t callback_recv;
static be32_t xid;
static sd_event_source *test_hangcheck;
-static int test_dhcp_hangcheck(sd_event_source *s, uint64_t usec,
- void *userdata)
-{
+static int test_dhcp_hangcheck(sd_event_source *s, uint64_t usec, void *userdata) {
assert_not_reached("Test case should have completed in 2 seconds");
return 0;
}
-static void test_request_basic(sd_event *e)
-{
+static void test_request_basic(sd_event *e) {
int r;
sd_dhcp_client *client;
@@ -70,38 +66,35 @@ static void test_request_basic(sd_event *e)
assert_se(sd_dhcp_client_set_request_option(NULL, 0) == -EINVAL);
assert_se(sd_dhcp_client_set_request_address(NULL, NULL) == -EINVAL);
- assert_se(sd_dhcp_client_set_index(NULL, 0) == -EINVAL);
+ assert_se(sd_dhcp_client_set_ifindex(NULL, 0) == -EINVAL);
- assert_se(sd_dhcp_client_set_index(client, 15) == 0);
- assert_se(sd_dhcp_client_set_index(client, -42) == -EINVAL);
- assert_se(sd_dhcp_client_set_index(client, -1) == -EINVAL);
- assert_se(sd_dhcp_client_set_index(client, 0) == -EINVAL);
- assert_se(sd_dhcp_client_set_index(client, 1) == 0);
+ assert_se(sd_dhcp_client_set_ifindex(client, 15) == 0);
+ assert_se(sd_dhcp_client_set_ifindex(client, -42) == -EINVAL);
+ assert_se(sd_dhcp_client_set_ifindex(client, -1) == -EINVAL);
+ assert_se(sd_dhcp_client_set_ifindex(client, 0) == -EINVAL);
+ assert_se(sd_dhcp_client_set_ifindex(client, 1) == 0);
assert_se(sd_dhcp_client_set_request_option(client,
- DHCP_OPTION_SUBNET_MASK) == -EEXIST);
+ SD_DHCP_OPTION_SUBNET_MASK) == -EEXIST);
assert_se(sd_dhcp_client_set_request_option(client,
- DHCP_OPTION_ROUTER) == -EEXIST);
+ SD_DHCP_OPTION_ROUTER) == -EEXIST);
assert_se(sd_dhcp_client_set_request_option(client,
- DHCP_OPTION_HOST_NAME) == -EEXIST);
+ SD_DHCP_OPTION_HOST_NAME) == -EEXIST);
assert_se(sd_dhcp_client_set_request_option(client,
- DHCP_OPTION_DOMAIN_NAME) == -EEXIST);
+ SD_DHCP_OPTION_DOMAIN_NAME) == -EEXIST);
assert_se(sd_dhcp_client_set_request_option(client,
- DHCP_OPTION_DOMAIN_NAME_SERVER)
- == -EEXIST);
- assert_se(sd_dhcp_client_set_request_option(client,
- DHCP_OPTION_NTP_SERVER) == -EEXIST);
+ SD_DHCP_OPTION_DOMAIN_NAME_SERVER) == -EEXIST);
assert_se(sd_dhcp_client_set_request_option(client,
- DHCP_OPTION_PAD) == -EINVAL);
+ SD_DHCP_OPTION_PAD) == -EINVAL);
assert_se(sd_dhcp_client_set_request_option(client,
- DHCP_OPTION_END) == -EINVAL);
+ SD_DHCP_OPTION_END) == -EINVAL);
assert_se(sd_dhcp_client_set_request_option(client,
- DHCP_OPTION_MESSAGE_TYPE) == -EINVAL);
+ SD_DHCP_OPTION_MESSAGE_TYPE) == -EINVAL);
assert_se(sd_dhcp_client_set_request_option(client,
- DHCP_OPTION_OVERLOAD) == -EINVAL);
+ SD_DHCP_OPTION_OVERLOAD) == -EINVAL);
assert_se(sd_dhcp_client_set_request_option(client,
- DHCP_OPTION_PARAMETER_REQUEST_LIST)
+ SD_DHCP_OPTION_PARAMETER_REQUEST_LIST)
== -EINVAL);
assert_se(sd_dhcp_client_set_request_option(client, 33) == 0);
@@ -112,8 +105,7 @@ static void test_request_basic(sd_event *e)
sd_dhcp_client_unref(client);
}
-static void test_checksum(void)
-{
+static void test_checksum(void) {
uint8_t buf[20] = {
0x45, 0x00, 0x02, 0x40, 0x00, 0x00, 0x00, 0x00,
0x40, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
@@ -126,11 +118,9 @@ static void test_checksum(void)
assert_se(dhcp_packet_checksum((uint8_t*)&buf, 20) == be16toh(0x78ae));
}
-static int check_options(uint8_t code, uint8_t len, const uint8_t *option,
- void *user_data)
-{
+static int check_options(uint8_t code, uint8_t len, const void *option, void *userdata) {
switch(code) {
- case DHCP_OPTION_CLIENT_IDENTIFIER:
+ case SD_DHCP_OPTION_CLIENT_IDENTIFIER:
{
uint32_t iaid;
struct duid duid;
@@ -141,10 +131,10 @@ static int check_options(uint8_t code, uint8_t len, const uint8_t *option,
assert_se(len == sizeof(uint8_t) + sizeof(uint32_t) + duid_len);
assert_se(len == 19);
- assert_se(option[0] == 0xff);
+ assert_se(((uint8_t*) option)[0] == 0xff);
- assert_se(memcmp(&option[1], &iaid, sizeof(iaid)) == 0);
- assert_se(memcmp(&option[5], &duid, duid_len) == 0);
+ assert_se(memcmp((uint8_t*) option + 1, &iaid, sizeof(iaid)) == 0);
+ assert_se(memcmp((uint8_t*) option + 5, &duid, duid_len) == 0);
break;
}
@@ -155,9 +145,7 @@ static int check_options(uint8_t code, uint8_t len, const uint8_t *option,
return 0;
}
-int dhcp_network_send_raw_socket(int s, const union sockaddr_union *link,
- const void *packet, size_t len)
-{
+int dhcp_network_send_raw_socket(int s, const union sockaddr_union *link, const void *packet, size_t len) {
size_t size;
_cleanup_free_ DHCPPacket *discover;
uint16_t ip_check, udp_check;
@@ -202,18 +190,20 @@ int dhcp_network_send_raw_socket(int s, const union sockaddr_union *link,
return 575;
}
-int dhcp_network_bind_raw_socket(int index, union sockaddr_union *link,
- uint32_t id, const uint8_t *addr,
- size_t addr_len, uint16_t arp_type)
-{
+int dhcp_network_bind_raw_socket(
+ int index,
+ union sockaddr_union *link,
+ uint32_t id,
+ const uint8_t *addr, size_t addr_len,
+ uint16_t arp_type) {
+
if (socketpair(AF_UNIX, SOCK_STREAM, 0, test_fd) < 0)
return -errno;
return test_fd[0];
}
-int dhcp_network_bind_udp_socket(be32_t address, uint16_t port)
-{
+int dhcp_network_bind_udp_socket(be32_t address, uint16_t port) {
int fd;
fd = socket(AF_INET, SOCK_DGRAM|SOCK_CLOEXEC, 0);
@@ -223,17 +213,14 @@ int dhcp_network_bind_udp_socket(be32_t address, uint16_t port)
return fd;
}
-int dhcp_network_send_udp_socket(int s, be32_t address, uint16_t port,
- const void *packet, size_t len)
-{
+int dhcp_network_send_udp_socket(int s, be32_t address, uint16_t port, const void *packet, size_t len) {
return 0;
}
-static int test_discover_message_verify(size_t size, struct DHCPMessage *dhcp)
-{
+static int test_discover_message_verify(size_t size, struct DHCPMessage *dhcp) {
int res;
- res = dhcp_option_parse(dhcp, size, check_options, NULL);
+ res = dhcp_option_parse(dhcp, size, check_options, NULL, NULL);
assert_se(res == DHCP_DISCOVER);
if (verbose)
@@ -242,8 +229,7 @@ static int test_discover_message_verify(size_t size, struct DHCPMessage *dhcp)
return 0;
}
-static void test_discover_message(sd_event *e)
-{
+static void test_discover_message(sd_event *e) {
sd_dhcp_client *client;
int res, r;
@@ -257,7 +243,7 @@ static void test_discover_message(sd_event *e)
r = sd_dhcp_client_attach_event(client, e, 0);
assert_se(r >= 0);
- assert_se(sd_dhcp_client_set_index(client, 42) >= 0);
+ assert_se(sd_dhcp_client_set_ifindex(client, 42) >= 0);
assert_se(sd_dhcp_client_set_mac(client, mac_addr, ETH_ALEN, ARPHRD_ETHER) >= 0);
assert_se(sd_dhcp_client_set_request_option(client, 248) >= 0);
@@ -373,7 +359,7 @@ static void test_addr_acq_acquired(sd_dhcp_client *client, int event,
struct in_addr addr;
assert_se(client);
- assert_se(event == DHCP_EVENT_IP_ACQUIRE);
+ assert_se(event == SD_DHCP_CLIENT_EVENT_IP_ACQUIRE);
assert_se(sd_dhcp_client_get_lease(client, &lease) >= 0);
assert_se(lease);
@@ -393,7 +379,6 @@ static void test_addr_acq_acquired(sd_dhcp_client *client, int event,
if (verbose)
printf(" DHCP address acquired\n");
- sd_dhcp_lease_unref(lease);
sd_event_exit(e, 0);
}
@@ -402,11 +387,11 @@ static int test_addr_acq_recv_request(size_t size, DHCPMessage *request) {
uint8_t *msg_bytes = (uint8_t *)request;
int res;
- res = dhcp_option_parse(request, size, check_options, NULL);
+ res = dhcp_option_parse(request, size, check_options, NULL, NULL);
assert_se(res == DHCP_REQUEST);
assert_se(xid == request->xid);
- assert_se(msg_bytes[size - 1] == DHCP_OPTION_END);
+ assert_se(msg_bytes[size - 1] == SD_DHCP_OPTION_END);
if (verbose)
printf(" recv DHCP Request 0x%08x\n", be32toh(xid));
@@ -432,10 +417,10 @@ static int test_addr_acq_recv_discover(size_t size, DHCPMessage *discover) {
uint8_t *msg_bytes = (uint8_t *)discover;
int res;
- res = dhcp_option_parse(discover, size, check_options, NULL);
+ res = dhcp_option_parse(discover, size, check_options, NULL, NULL);
assert_se(res == DHCP_DISCOVER);
- assert_se(msg_bytes[size - 1] == DHCP_OPTION_END);
+ assert_se(msg_bytes[size - 1] == SD_DHCP_OPTION_END);
xid = discover->xid;
@@ -473,7 +458,7 @@ static void test_addr_acq(sd_event *e) {
r = sd_dhcp_client_attach_event(client, e, 0);
assert_se(r >= 0);
- assert_se(sd_dhcp_client_set_index(client, 42) >= 0);
+ assert_se(sd_dhcp_client_set_ifindex(client, 42) >= 0);
assert_se(sd_dhcp_client_set_mac(client, mac_addr, ETH_ALEN, ARPHRD_ETHER) >= 0);
assert_se(sd_dhcp_client_set_callback(client, test_addr_acq_acquired, e) >= 0);
@@ -503,7 +488,7 @@ static void test_addr_acq(sd_event *e) {
}
int main(int argc, char *argv[]) {
- _cleanup_event_unref_ sd_event *e;
+ _cleanup_(sd_event_unrefp) sd_event *e;
log_set_max_level(LOG_DEBUG);
log_parse_environment();
diff --git a/src/libsystemd-network/test-dhcp-option.c b/src/libsystemd-network/test-dhcp-option.c
index a63a4ea738..d84859c053 100644
--- a/src/libsystemd-network/test-dhcp-option.c
+++ b/src/libsystemd-network/test-dhcp-option.c
@@ -1,15 +1,13 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-#include <stdio.h>
-#include <stdbool.h>
#include <errno.h>
+#include <stdbool.h>
+#include <stdio.h>
#include <string.h>
-#include "util.h"
-#include "macro.h"
-
-#include "dhcp-protocol.h"
+#include "alloc-util.h"
#include "dhcp-internal.h"
+#include "dhcp-protocol.h"
+#include "macro.h"
+#include "util.h"
struct option_desc {
uint8_t sname[64];
@@ -29,7 +27,7 @@ static bool verbose = false;
static struct option_desc option_tests[] = {
{ {}, 0, {}, 0, { 42, 5, 65, 66, 67, 68, 69 }, 7, false, },
{ {}, 0, {}, 0, { 42, 5, 65, 66, 67, 68, 69, 0, 0,
- DHCP_OPTION_MESSAGE_TYPE, 1, DHCP_ACK }, 12, true, },
+ SD_DHCP_OPTION_MESSAGE_TYPE, 1, DHCP_ACK }, 12, true, },
{ {}, 0, {}, 0, { 8, 255, 70, 71, 72 }, 5, false, },
{ {}, 0, {}, 0, { 0x35, 0x01, 0x05, 0x36, 0x04, 0x01, 0x00, 0xa8,
0xc0, 0x33, 0x04, 0x00, 0x01, 0x51, 0x80, 0x01,
@@ -37,17 +35,17 @@ static struct option_desc option_tests[] = {
0xa8, 0x00, 0x01, 0x06, 0x04, 0xc0, 0xa8, 0x00,
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, },
40, true, },
- { {}, 0, {}, 0, { DHCP_OPTION_MESSAGE_TYPE, 1, DHCP_OFFER,
+ { {}, 0, {}, 0, { SD_DHCP_OPTION_MESSAGE_TYPE, 1, DHCP_OFFER,
42, 3, 0, 0, 0 }, 8, true, },
{ {}, 0, {}, 0, { 42, 2, 1, 2, 44 }, 5, false, },
{ {}, 0,
- { 222, 3, 1, 2, 3, DHCP_OPTION_MESSAGE_TYPE, 1, DHCP_NAK }, 8,
- { DHCP_OPTION_OVERLOAD, 1, DHCP_OVERLOAD_FILE }, 3, true, },
+ { 222, 3, 1, 2, 3, SD_DHCP_OPTION_MESSAGE_TYPE, 1, DHCP_NAK }, 8,
+ { SD_DHCP_OPTION_OVERLOAD, 1, DHCP_OVERLOAD_FILE }, 3, true, },
- { { 1, 4, 1, 2, 3, 4, DHCP_OPTION_MESSAGE_TYPE, 1, DHCP_ACK }, 9,
+ { { 1, 4, 1, 2, 3, 4, SD_DHCP_OPTION_MESSAGE_TYPE, 1, DHCP_ACK }, 9,
{ 222, 3, 1, 2, 3 }, 5,
- { DHCP_OPTION_OVERLOAD, 1,
+ { SD_DHCP_OPTION_OVERLOAD, 1,
DHCP_OVERLOAD_FILE|DHCP_OVERLOAD_SNAME }, 3, true, },
};
@@ -75,9 +73,8 @@ static const char *dhcp_type(int type) {
static void test_invalid_buffer_length(void) {
DHCPMessage message;
- assert_se(dhcp_option_parse(&message, 0, NULL, NULL) == -EINVAL);
- assert_se(dhcp_option_parse(&message, sizeof(DHCPMessage) - 1, NULL, NULL)
- == -EINVAL);
+ assert_se(dhcp_option_parse(&message, 0, NULL, NULL, NULL) == -EINVAL);
+ assert_se(dhcp_option_parse(&message, sizeof(DHCPMessage) - 1, NULL, NULL, NULL) == -EINVAL);
}
static void test_message_init(void) {
@@ -101,7 +98,7 @@ static void test_message_init(void) {
assert_se(magic[2] == 83);
assert_se(magic[3] == 99);
- assert_se(dhcp_option_parse(message, len, NULL, NULL) >= 0);
+ assert_se(dhcp_option_parse(message, len, NULL, NULL, NULL) >= 0);
}
static DHCPMessage *create_message(uint8_t *options, uint16_t optlen,
@@ -113,14 +110,9 @@ static DHCPMessage *create_message(uint8_t *options, uint16_t optlen,
message = malloc0(len);
assert_se(message);
- if (options && optlen)
- memcpy(&message->options, options, optlen);
-
- if (file && filelen <= 128)
- memcpy(&message->file, file, filelen);
-
- if (sname && snamelen <= 64)
- memcpy(&message->sname, sname, snamelen);
+ memcpy_safe(&message->options, options, optlen);
+ memcpy_safe(&message->file, file, filelen);
+ memcpy_safe(&message->sname, sname, snamelen);
return message;
}
@@ -130,12 +122,12 @@ static void test_ignore_opts(uint8_t *descoption, int *descpos, int *desclen) {
while (*descpos < *desclen) {
switch(descoption[*descpos]) {
- case DHCP_OPTION_PAD:
+ case SD_DHCP_OPTION_PAD:
*descpos += 1;
break;
- case DHCP_OPTION_MESSAGE_TYPE:
- case DHCP_OPTION_OVERLOAD:
+ case SD_DHCP_OPTION_MESSAGE_TYPE:
+ case SD_DHCP_OPTION_OVERLOAD:
*descpos += 3;
break;
@@ -145,8 +137,8 @@ static void test_ignore_opts(uint8_t *descoption, int *descpos, int *desclen) {
}
}
-static int test_options_cb(uint8_t code, uint8_t len, const uint8_t *option, void *user_data) {
- struct option_desc *desc = user_data;
+static int test_options_cb(uint8_t code, uint8_t len, const void *option, void *userdata) {
+ struct option_desc *desc = userdata;
uint8_t *descoption = NULL;
int *desclen = NULL, *descpos = NULL;
uint8_t optcode = 0;
@@ -158,10 +150,10 @@ static int test_options_cb(uint8_t code, uint8_t len, const uint8_t *option, voi
if (!desc)
return -EINVAL;
- assert_se(code != DHCP_OPTION_PAD);
- assert_se(code != DHCP_OPTION_END);
- assert_se(code != DHCP_OPTION_MESSAGE_TYPE);
- assert_se(code != DHCP_OPTION_OVERLOAD);
+ assert_se(code != SD_DHCP_OPTION_PAD);
+ assert_se(code != SD_DHCP_OPTION_END);
+ assert_se(code != SD_DHCP_OPTION_MESSAGE_TYPE);
+ assert_se(code != SD_DHCP_OPTION_OVERLOAD);
while (desc->pos >= 0 || desc->filepos >= 0 || desc->snamepos >= 0) {
@@ -207,10 +199,10 @@ static int test_options_cb(uint8_t code, uint8_t len, const uint8_t *option, voi
for (i = 0; i < len; i++) {
if (verbose)
- printf("0x%02x(0x%02x) ", option[i],
+ printf("0x%02x(0x%02x) ", ((uint8_t*) option)[i],
descoption[*descpos + 2 + i]);
- assert_se(option[i] == descoption[*descpos + 2 + i]);
+ assert_se(((uint8_t*) option)[i] == descoption[*descpos + 2 + i]);
}
if (verbose)
@@ -264,19 +256,12 @@ static void test_options(struct option_desc *desc) {
buflen = sizeof(DHCPMessage) + optlen;
if (!desc) {
- assert_se((res = dhcp_option_parse(message, buflen,
- test_options_cb,
- NULL)) == -ENOMSG);
+ assert_se((res = dhcp_option_parse(message, buflen, test_options_cb, NULL, NULL)) == -ENOMSG);
} else if (desc->success) {
- assert_se((res = dhcp_option_parse(message, buflen,
- test_options_cb,
- desc)) >= 0);
- assert_se(desc->pos == -1 && desc->filepos == -1 &&
- desc->snamepos == -1);
+ assert_se((res = dhcp_option_parse(message, buflen, test_options_cb, desc, NULL)) >= 0);
+ assert_se(desc->pos == -1 && desc->filepos == -1 && desc->snamepos == -1);
} else
- assert_se((res = dhcp_option_parse(message, buflen,
- test_options_cb,
- desc)) < 0);
+ assert_se((res = dhcp_option_parse(message, buflen, test_options_cb, desc, NULL)) < 0);
if (verbose)
printf("DHCP type %s\n", dhcp_type(res));
@@ -306,27 +291,27 @@ static void test_option_set(void) {
result->options[2] = 'C';
result->options[3] = 'D';
- assert_se(dhcp_option_append(result, 0, &offset, 0, DHCP_OPTION_PAD,
+ assert_se(dhcp_option_append(result, 0, &offset, 0, SD_DHCP_OPTION_PAD,
0, NULL) == -ENOBUFS);
assert_se(offset == 0);
offset = 4;
- assert_se(dhcp_option_append(result, 5, &offset, 0, DHCP_OPTION_PAD,
+ assert_se(dhcp_option_append(result, 5, &offset, 0, SD_DHCP_OPTION_PAD,
0, NULL) == -ENOBUFS);
assert_se(offset == 4);
- assert_se(dhcp_option_append(result, 6, &offset, 0, DHCP_OPTION_PAD,
+ assert_se(dhcp_option_append(result, 6, &offset, 0, SD_DHCP_OPTION_PAD,
0, NULL) >= 0);
assert_se(offset == 5);
offset = pos = 4;
len = 11;
- while (pos < len && options[pos] != DHCP_OPTION_END) {
+ while (pos < len && options[pos] != SD_DHCP_OPTION_END) {
assert_se(dhcp_option_append(result, len, &offset, DHCP_OVERLOAD_SNAME,
options[pos],
options[pos + 1],
&options[pos + 2]) >= 0);
- if (options[pos] == DHCP_OPTION_PAD)
+ if (options[pos] == SD_DHCP_OPTION_PAD)
pos++;
else
pos += 2 + options[pos + 1];
@@ -344,15 +329,15 @@ static void test_option_set(void) {
if (verbose)
printf("%2d: 0x%02x(0x%02x) (options)\n", 9, result->options[9],
- DHCP_OPTION_END);
+ SD_DHCP_OPTION_END);
- assert_se(result->options[9] == DHCP_OPTION_END);
+ assert_se(result->options[9] == SD_DHCP_OPTION_END);
if (verbose)
printf("%2d: 0x%02x(0x%02x) (options)\n", 10, result->options[10],
- DHCP_OPTION_PAD);
+ SD_DHCP_OPTION_PAD);
- assert_se(result->options[10] == DHCP_OPTION_PAD);
+ assert_se(result->options[10] == SD_DHCP_OPTION_PAD);
for (i = 0; i < pos - 8; i++) {
if (verbose)
diff --git a/src/libsystemd-network/test-dhcp-server.c b/src/libsystemd-network/test-dhcp-server.c
index 9f60ab761e..e81c508c7f 100644
--- a/src/libsystemd-network/test-dhcp-server.c
+++ b/src/libsystemd-network/test-dhcp-server.c
@@ -1,5 +1,3 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
/***
This file is part of systemd.
@@ -22,14 +20,21 @@
#include <errno.h>
+#include "sd-dhcp-server.h"
#include "sd-event.h"
-#include "event-util.h"
-#include "sd-dhcp-server.h"
#include "dhcp-server-internal.h"
+static void test_pool(struct in_addr *address, unsigned size, int ret) {
+ _cleanup_(sd_dhcp_server_unrefp) sd_dhcp_server *server = NULL;
+
+ assert_se(sd_dhcp_server_new(&server, 1) >= 0);
+
+ assert_se(sd_dhcp_server_configure_pool(server, address, 8, 0, size) == ret);
+}
+
static int test_basic(sd_event *event) {
- _cleanup_dhcp_server_unref_ sd_dhcp_server *server = NULL;
+ _cleanup_(sd_dhcp_server_unrefp) sd_dhcp_server *server = NULL;
struct in_addr address_lo = {
.s_addr = htonl(INADDR_LOOPBACK),
};
@@ -54,15 +59,14 @@ static int test_basic(sd_event *event) {
assert_se(!sd_dhcp_server_unref(server));
assert_se(sd_dhcp_server_start(server) == -EUNATCH);
- assert_se(sd_dhcp_server_set_address(server, &address_any, 28) == -EINVAL);
- assert_se(sd_dhcp_server_set_address(server, &address_lo, 38) == -ERANGE);
- assert_se(sd_dhcp_server_set_address(server, &address_lo, 8) >= 0);
- assert_se(sd_dhcp_server_set_address(server, &address_lo, 8) == -EBUSY);
- assert_se(sd_dhcp_server_set_lease_pool(server, &address_any, 1) == -EINVAL);
- assert_se(sd_dhcp_server_set_lease_pool(server, &address_lo, 0) == -EINVAL);
- assert_se(sd_dhcp_server_set_lease_pool(server, &address_lo, 1) >= 0);
- assert_se(sd_dhcp_server_set_lease_pool(server, &address_lo, 1) == -EBUSY);
+ assert_se(sd_dhcp_server_configure_pool(server, &address_any, 28, 0, 0) == -EINVAL);
+ assert_se(sd_dhcp_server_configure_pool(server, &address_lo, 38, 0, 0) == -ERANGE);
+ assert_se(sd_dhcp_server_configure_pool(server, &address_lo, 8, 0, 0) >= 0);
+ assert_se(sd_dhcp_server_configure_pool(server, &address_lo, 8, 0, 0) == -EBUSY);
+
+ test_pool(&address_any, 1, -EINVAL);
+ test_pool(&address_lo, 1, 0);
r = sd_dhcp_server_start(server);
@@ -79,7 +83,7 @@ static int test_basic(sd_event *event) {
}
static void test_message_handler(void) {
- _cleanup_dhcp_server_unref_ sd_dhcp_server *server = NULL;
+ _cleanup_(sd_dhcp_server_unrefp) sd_dhcp_server *server = NULL;
struct {
DHCPMessage message;
struct {
@@ -109,35 +113,33 @@ static void test_message_handler(void) {
.message.hlen = ETHER_ADDR_LEN,
.message.xid = htobe32(0x12345678),
.message.chaddr = { 'A', 'B', 'C', 'D', 'E', 'F' },
- .option_type.code = DHCP_OPTION_MESSAGE_TYPE,
+ .option_type.code = SD_DHCP_OPTION_MESSAGE_TYPE,
.option_type.length = 1,
.option_type.type = DHCP_DISCOVER,
- .end = DHCP_OPTION_END,
+ .end = SD_DHCP_OPTION_END,
};
struct in_addr address_lo = {
.s_addr = htonl(INADDR_LOOPBACK),
};
assert_se(sd_dhcp_server_new(&server, 1) >= 0);
- assert_se(sd_dhcp_server_set_address(server, &address_lo, 8) >= 0);
+ assert_se(sd_dhcp_server_configure_pool(server, &address_lo, 8, 0, 0) >= 0);
assert_se(sd_dhcp_server_attach_event(server, NULL, 0) >= 0);
assert_se(sd_dhcp_server_start(server) >= 0);
- assert_se(dhcp_server_handle_message(server, (DHCPMessage*)&test, sizeof(test)) == 0);
- assert_se(sd_dhcp_server_set_lease_pool(server, &address_lo, 10) >= 0);
assert_se(dhcp_server_handle_message(server, (DHCPMessage*)&test, sizeof(test)) == DHCP_OFFER);
test.end = 0;
/* TODO, shouldn't this fail? */
assert_se(dhcp_server_handle_message(server, (DHCPMessage*)&test, sizeof(test)) == DHCP_OFFER);
- test.end = DHCP_OPTION_END;
+ test.end = SD_DHCP_OPTION_END;
assert_se(dhcp_server_handle_message(server, (DHCPMessage*)&test, sizeof(test)) == DHCP_OFFER);
test.option_type.code = 0;
test.option_type.length = 0;
test.option_type.type = 0;
assert_se(dhcp_server_handle_message(server, (DHCPMessage*)&test, sizeof(test)) == 0);
- test.option_type.code = DHCP_OPTION_MESSAGE_TYPE;
+ test.option_type.code = SD_DHCP_OPTION_MESSAGE_TYPE;
test.option_type.length = 1;
test.option_type.type = DHCP_DISCOVER;
assert_se(dhcp_server_handle_message(server, (DHCPMessage*)&test, sizeof(test)) == DHCP_OFFER);
@@ -159,11 +161,11 @@ static void test_message_handler(void) {
test.option_type.type = DHCP_REQUEST;
assert_se(dhcp_server_handle_message(server, (DHCPMessage*)&test, sizeof(test)) == 0);
- test.option_requested_ip.code = DHCP_OPTION_REQUESTED_IP_ADDRESS;
+ test.option_requested_ip.code = SD_DHCP_OPTION_REQUESTED_IP_ADDRESS;
test.option_requested_ip.length = 4;
test.option_requested_ip.address = htobe32(0x12345678);
assert_se(dhcp_server_handle_message(server, (DHCPMessage*)&test, sizeof(test)) == DHCP_NAK);
- test.option_server_id.code = DHCP_OPTION_SERVER_IDENTIFIER;
+ test.option_server_id.code = SD_DHCP_OPTION_SERVER_IDENTIFIER;
test.option_server_id.length = 4;
test.option_server_id.address = htobe32(INADDR_LOOPBACK);
test.option_requested_ip.address = htobe32(INADDR_LOOPBACK + 3);
@@ -178,7 +180,7 @@ static void test_message_handler(void) {
test.option_requested_ip.address = htobe32(INADDR_LOOPBACK + 3);
assert_se(dhcp_server_handle_message(server, (DHCPMessage*)&test, sizeof(test)) == DHCP_ACK);
- test.option_client_id.code = DHCP_OPTION_CLIENT_IDENTIFIER;
+ test.option_client_id.code = SD_DHCP_OPTION_CLIENT_IDENTIFIER;
test.option_client_id.length = 7;
test.option_client_id.id[0] = 0x01;
test.option_client_id.id[1] = 'A';
@@ -193,6 +195,15 @@ static void test_message_handler(void) {
assert_se(dhcp_server_handle_message(server, (DHCPMessage*)&test, sizeof(test)) == 0);
}
+static uint64_t client_id_hash_helper(DHCPClientId *id, uint8_t key[HASH_KEY_SIZE]) {
+ struct siphash state;
+
+ siphash24_init(&state, key);
+ client_id_hash_func(id, &state);
+
+ return htole64(siphash24_finalize(&state));
+}
+
static void test_client_id_hash(void) {
DHCPClientId a = {
.length = 4,
@@ -208,18 +219,18 @@ static void test_client_id_hash(void) {
b.data = (uint8_t*)strdup("abcd");
assert_se(client_id_compare_func(&a, &b) == 0);
- assert_se(client_id_hash_func(&a, hash_key) == client_id_hash_func(&b, hash_key));
+ assert_se(client_id_hash_helper(&a, hash_key) == client_id_hash_helper(&b, hash_key));
a.length = 3;
assert_se(client_id_compare_func(&a, &b) != 0);
a.length = 4;
assert_se(client_id_compare_func(&a, &b) == 0);
- assert_se(client_id_hash_func(&a, hash_key) == client_id_hash_func(&b, hash_key));
+ assert_se(client_id_hash_helper(&a, hash_key) == client_id_hash_helper(&b, hash_key));
b.length = 3;
assert_se(client_id_compare_func(&a, &b) != 0);
b.length = 4;
assert_se(client_id_compare_func(&a, &b) == 0);
- assert_se(client_id_hash_func(&a, hash_key) == client_id_hash_func(&b, hash_key));
+ assert_se(client_id_hash_helper(&a, hash_key) == client_id_hash_helper(&b, hash_key));
free(b.data);
b.data = (uint8_t*)strdup("abce");
@@ -230,7 +241,7 @@ static void test_client_id_hash(void) {
}
int main(int argc, char *argv[]) {
- _cleanup_event_unref_ sd_event *e;
+ _cleanup_(sd_event_unrefp) sd_event *e;
int r;
log_set_max_level(LOG_DEBUG);
diff --git a/src/libsystemd-network/test-dhcp6-client.c b/src/libsystemd-network/test-dhcp6-client.c
index 761854714b..bd289fa802 100644
--- a/src/libsystemd-network/test-dhcp6-client.c
+++ b/src/libsystemd-network/test-dhcp6-client.c
@@ -1,5 +1,3 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
/***
This file is part of systemd.
@@ -19,23 +17,23 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
+#include <net/ethernet.h>
#include <stdbool.h>
#include <stdio.h>
-#include <sys/types.h>
#include <sys/socket.h>
+#include <sys/types.h>
#include <unistd.h>
-#include <net/ethernet.h>
-#include "socket-util.h"
-#include "macro.h"
+#include "sd-dhcp6-client.h"
#include "sd-event.h"
-#include "event-util.h"
-#include "virt.h"
-#include "sd-dhcp6-client.h"
-#include "dhcp6-protocol.h"
#include "dhcp6-internal.h"
#include "dhcp6-lease-internal.h"
+#include "dhcp6-protocol.h"
+#include "fd-util.h"
+#include "macro.h"
+#include "socket-util.h"
+#include "virt.h"
static struct ether_addr mac_addr = {
.ether_addr_octet = {'A', 'B', 'C', '1', '2', '3'}
@@ -61,20 +59,20 @@ static int test_client_basic(sd_event *e) {
assert_se(sd_dhcp6_client_attach_event(client, e, 0) >= 0);
- assert_se(sd_dhcp6_client_set_index(client, 15) == 0);
- assert_se(sd_dhcp6_client_set_index(client, -42) == -EINVAL);
- assert_se(sd_dhcp6_client_set_index(client, -1) == 0);
- assert_se(sd_dhcp6_client_set_index(client, 42) >= 0);
+ assert_se(sd_dhcp6_client_set_ifindex(client, 15) == 0);
+ assert_se(sd_dhcp6_client_set_ifindex(client, -42) == -EINVAL);
+ assert_se(sd_dhcp6_client_set_ifindex(client, -1) == 0);
+ assert_se(sd_dhcp6_client_set_ifindex(client, 42) >= 0);
assert_se(sd_dhcp6_client_set_mac(client, (const uint8_t *) &mac_addr,
sizeof (mac_addr),
ARPHRD_ETHER) >= 0);
- assert_se(sd_dhcp6_client_set_request_option(client, DHCP6_OPTION_CLIENTID) == -EINVAL);
- assert_se(sd_dhcp6_client_set_request_option(client, DHCP6_OPTION_DNS_SERVERS) == -EEXIST);
- assert_se(sd_dhcp6_client_set_request_option(client, DHCP6_OPTION_NTP_SERVER) == -EEXIST);
- assert_se(sd_dhcp6_client_set_request_option(client, DHCP6_OPTION_SNTP_SERVERS) == 0);
- assert_se(sd_dhcp6_client_set_request_option(client, DHCP6_OPTION_DOMAIN_LIST) == -EEXIST);
+ assert_se(sd_dhcp6_client_set_request_option(client, SD_DHCP6_OPTION_CLIENTID) == -EINVAL);
+ assert_se(sd_dhcp6_client_set_request_option(client, SD_DHCP6_OPTION_DNS_SERVERS) == -EEXIST);
+ assert_se(sd_dhcp6_client_set_request_option(client, SD_DHCP6_OPTION_NTP_SERVER) == -EEXIST);
+ assert_se(sd_dhcp6_client_set_request_option(client, SD_DHCP6_OPTION_SNTP_SERVERS) == -EEXIST);
+ assert_se(sd_dhcp6_client_set_request_option(client, SD_DHCP6_OPTION_DOMAIN_LIST) == -EEXIST);
assert_se(sd_dhcp6_client_set_request_option(client, 10) == -EINVAL);
assert_se(sd_dhcp6_client_set_callback(client, NULL, NULL) >= 0);
@@ -88,9 +86,9 @@ static int test_client_basic(sd_event *e) {
static int test_option(sd_event *e) {
uint8_t packet[] = {
'F', 'O', 'O',
- 0x00, DHCP6_OPTION_ORO, 0x00, 0x07,
+ 0x00, SD_DHCP6_OPTION_ORO, 0x00, 0x07,
'A', 'B', 'C', 'D', 'E', 'F', 'G',
- 0x00, DHCP6_OPTION_VENDOR_CLASS, 0x00, 0x09,
+ 0x00, SD_DHCP6_OPTION_VENDOR_CLASS, 0x00, 0x09,
'1', '2', '3', '4', '5', '6', '7', '8', '9',
'B', 'A', 'R',
};
@@ -124,7 +122,7 @@ static int test_option(sd_event *e) {
&optval) >= 0);
pos += 4 + optlen;
assert_se(buf == &packet[pos]);
- assert_se(optcode == DHCP6_OPTION_ORO);
+ assert_se(optcode == SD_DHCP6_OPTION_ORO);
assert_se(optlen == 7);
assert_se(buflen + pos == sizeof(packet));
@@ -137,7 +135,7 @@ static int test_option(sd_event *e) {
&optval) >= 0);
pos += 4 + optlen;
assert_se(buf == &packet[pos]);
- assert_se(optcode == DHCP6_OPTION_VENDOR_CLASS);
+ assert_se(optcode == SD_DHCP6_OPTION_VENDOR_CLASS);
assert_se(optlen == 9);
assert_se(buflen + pos == sizeof(packet));
@@ -205,7 +203,7 @@ static uint8_t msg_reply[173] = {
};
static int test_advertise_option(sd_event *e) {
- _cleanup_dhcp6_lease_free_ sd_dhcp6_lease *lease = NULL;
+ _cleanup_(sd_dhcp6_lease_unrefp) sd_dhcp6_lease *lease = NULL;
DHCP6Message *advertise = (DHCP6Message *)msg_advertise;
uint8_t *optval, *opt = msg_advertise + sizeof(DHCP6Message);
uint16_t optcode;
@@ -216,6 +214,8 @@ static int test_advertise_option(sd_event *e) {
uint32_t lt_pref, lt_valid;
int r;
bool opt_clientid = false;
+ struct in6_addr *addrs;
+ char **domains;
if (verbose)
printf("* %s\n", __FUNCTION__);
@@ -230,13 +230,13 @@ static int test_advertise_option(sd_event *e) {
&optval)) >= 0) {
switch(optcode) {
- case DHCP6_OPTION_CLIENTID:
+ case SD_DHCP6_OPTION_CLIENTID:
assert_se(optlen == 14);
opt_clientid = true;
break;
- case DHCP6_OPTION_IA_NA:
+ case SD_DHCP6_OPTION_IA_NA:
assert_se(optlen == 94);
assert_se(!memcmp(optval, &msg_advertise[26], optlen));
@@ -255,7 +255,7 @@ static int test_advertise_option(sd_event *e) {
break;
- case DHCP6_OPTION_SERVERID:
+ case SD_DHCP6_OPTION_SERVERID:
assert_se(optlen == 14);
assert_se(!memcmp(optval, &msg_advertise[179], optlen));
@@ -263,7 +263,7 @@ static int test_advertise_option(sd_event *e) {
optlen) >= 0);
break;
- case DHCP6_OPTION_PREFERENCE:
+ case SD_DHCP6_OPTION_PREFERENCE:
assert_se(optlen == 1);
assert_se(!*optval);
@@ -271,11 +271,29 @@ static int test_advertise_option(sd_event *e) {
*optval) >= 0);
break;
- case DHCP6_OPTION_ELAPSED_TIME:
+ case SD_DHCP6_OPTION_ELAPSED_TIME:
assert_se(optlen == 2);
break;
+ case SD_DHCP6_OPTION_DNS_SERVERS:
+ assert_se(optlen == 16);
+ assert_se(dhcp6_lease_set_dns(lease, optval,
+ optlen) >= 0);
+ break;
+
+ case SD_DHCP6_OPTION_DOMAIN_LIST:
+ assert_se(optlen == 11);
+ assert_se(dhcp6_lease_set_domains(lease, optval,
+ optlen) >= 0);
+ break;
+
+ case SD_DHCP6_OPTION_SNTP_SERVERS:
+ assert_se(optlen == 16);
+ assert_se(dhcp6_lease_set_sntp(lease, optval,
+ optlen) >= 0);
+ break;
+
default:
break;
}
@@ -315,6 +333,19 @@ static int test_advertise_option(sd_event *e) {
assert_se(dhcp6_lease_get_preference(lease, &preference) >= 0);
assert_se(preference == 0);
+ r = sd_dhcp6_lease_get_dns(lease, &addrs);
+ assert_se(r == 1);
+ assert_se(!memcmp(addrs, &msg_advertise[124], r * 16));
+
+ r = sd_dhcp6_lease_get_domains(lease, &domains);
+ assert_se(r == 1);
+ assert_se(!strcmp("lab.intra", domains[0]));
+ assert_se(domains[1] == NULL);
+
+ r = sd_dhcp6_lease_get_ntp_addrs(lease, &addrs);
+ assert_se(r == 1);
+ assert_se(!memcmp(addrs, &msg_advertise[159], r * 16));
+
return 0;
}
@@ -324,26 +355,29 @@ static int test_hangcheck(sd_event_source *s, uint64_t usec, void *userdata) {
return 0;
}
-int detect_vm(const char **id) {
- return 1;
-}
-
-int detect_container(const char **id) {
- return 1;
-}
-
-int detect_virtualization(const char **id) {
- return 1;
-}
-
static void test_client_solicit_cb(sd_dhcp6_client *client, int event,
void *userdata) {
sd_event *e = userdata;
+ sd_dhcp6_lease *lease;
+ struct in6_addr *addrs;
+ char **domains;
assert_se(e);
- assert_se(event == DHCP6_EVENT_IP_ACQUIRE);
+ assert_se(event == SD_DHCP6_CLIENT_EVENT_IP_ACQUIRE);
+
+ assert_se(sd_dhcp6_client_get_lease(client, &lease) >= 0);
- assert_se(sd_dhcp6_client_set_request_option(client, DHCP6_OPTION_DNS_SERVERS) == -EBUSY);
+ assert_se(sd_dhcp6_lease_get_domains(lease, &domains) == 1);
+ assert_se(!strcmp("lab.intra", domains[0]));
+ assert_se(domains[1] == NULL);
+
+ assert_se(sd_dhcp6_lease_get_dns(lease, &addrs) == 1);
+ assert_se(!memcmp(addrs, &msg_advertise[124], 16));
+
+ assert_se(sd_dhcp6_lease_get_ntp_addrs(lease, &addrs) == 1);
+ assert_se(!memcmp(addrs, &msg_advertise[159], 16));
+
+ assert_se(sd_dhcp6_client_set_request_option(client, SD_DHCP6_OPTION_DNS_SERVERS) == -EBUSY);
if (verbose)
printf(" got DHCPv6 event %d\n", event);
@@ -371,7 +405,7 @@ static int test_client_send_reply(DHCP6Message *request) {
static int test_client_verify_request(DHCP6Message *request, uint8_t *option,
size_t len) {
- _cleanup_dhcp6_lease_free_ sd_dhcp6_lease *lease = NULL;
+ _cleanup_(sd_dhcp6_lease_unrefp) sd_dhcp6_lease *lease = NULL;
uint8_t *optval;
uint16_t optcode;
size_t optlen;
@@ -389,7 +423,7 @@ static int test_client_verify_request(DHCP6Message *request, uint8_t *option,
while ((r = dhcp6_option_parse(&option, &len,
&optcode, &optlen, &optval)) >= 0) {
switch(optcode) {
- case DHCP6_OPTION_CLIENTID:
+ case SD_DHCP6_OPTION_CLIENTID:
assert_se(!found_clientid);
found_clientid = true;
@@ -398,7 +432,7 @@ static int test_client_verify_request(DHCP6Message *request, uint8_t *option,
break;
- case DHCP6_OPTION_IA_NA:
+ case SD_DHCP6_OPTION_IA_NA:
assert_se(!found_iana);
found_iana = true;
@@ -417,7 +451,7 @@ static int test_client_verify_request(DHCP6Message *request, uint8_t *option,
break;
- case DHCP6_OPTION_SERVERID:
+ case SD_DHCP6_OPTION_SERVERID:
assert_se(!found_serverid);
found_serverid = true;
@@ -426,7 +460,7 @@ static int test_client_verify_request(DHCP6Message *request, uint8_t *option,
break;
- case DHCP6_OPTION_ELAPSED_TIME:
+ case SD_DHCP6_OPTION_ELAPSED_TIME:
assert_se(!found_elapsed_time);
found_elapsed_time = true;
@@ -453,8 +487,7 @@ static int test_client_verify_request(DHCP6Message *request, uint8_t *option,
return 0;
}
-static int test_client_send_advertise(DHCP6Message *solicit)
-{
+static int test_client_send_advertise(DHCP6Message *solicit) {
DHCP6Message advertise;
advertise.transaction_id = solicit->transaction_id;
@@ -486,7 +519,7 @@ static int test_client_verify_solicit(DHCP6Message *solicit, uint8_t *option,
while ((r = dhcp6_option_parse(&option, &len,
&optcode, &optlen, &optval)) >= 0) {
switch(optcode) {
- case DHCP6_OPTION_CLIENTID:
+ case SD_DHCP6_OPTION_CLIENTID:
assert_se(!found_clientid);
found_clientid = true;
@@ -495,7 +528,7 @@ static int test_client_verify_solicit(DHCP6Message *solicit, uint8_t *option,
break;
- case DHCP6_OPTION_IA_NA:
+ case SD_DHCP6_OPTION_IA_NA:
assert_se(!found_iana);
found_iana = true;
@@ -505,7 +538,7 @@ static int test_client_verify_solicit(DHCP6Message *solicit, uint8_t *option,
break;
- case DHCP6_OPTION_ELAPSED_TIME:
+ case SD_DHCP6_OPTION_ELAPSED_TIME:
assert_se(!found_elapsed_time);
found_elapsed_time = true;
@@ -524,24 +557,46 @@ static int test_client_verify_solicit(DHCP6Message *solicit, uint8_t *option,
static void test_client_information_cb(sd_dhcp6_client *client, int event,
void *userdata) {
sd_event *e = userdata;
+ sd_dhcp6_lease *lease;
+ struct in6_addr *addrs;
+ struct in6_addr address = { { { 0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x01 } } };
+ char **domains;
assert_se(e);
- assert_se(event == DHCP6_EVENT_INFORMATION_REQUEST);
+ assert_se(event == SD_DHCP6_CLIENT_EVENT_INFORMATION_REQUEST);
+
+ assert_se(sd_dhcp6_client_get_lease(client, &lease) >= 0);
+
+ assert_se(sd_dhcp6_lease_get_domains(lease, &domains) == 1);
+ assert_se(!strcmp("lab.intra", domains[0]));
+ assert_se(domains[1] == NULL);
+
+ assert_se(sd_dhcp6_lease_get_dns(lease, &addrs) == 1);
+ assert_se(!memcmp(addrs, &msg_advertise[124], 16));
+
+ assert_se(sd_dhcp6_lease_get_ntp_addrs(lease, &addrs) == 1);
+ assert_se(!memcmp(addrs, &msg_advertise[159], 16));
if (verbose)
printf(" got DHCPv6 event %d\n", event);
+ assert_se(sd_dhcp6_client_set_information_request(client, false) == -EBUSY);
+ assert_se(sd_dhcp6_client_set_callback(client, NULL, e) >= 0);
+ assert_se(sd_dhcp6_client_stop(client) >= 0);
assert_se(sd_dhcp6_client_set_information_request(client, false) >= 0);
+
assert_se(sd_dhcp6_client_set_callback(client,
test_client_solicit_cb, e) >= 0);
+ assert_se(sd_dhcp6_client_set_local_address(client, &address) >= 0);
+
assert_se(sd_dhcp6_client_start(client) >= 0);
}
static int test_client_verify_information_request(DHCP6Message *information_request,
uint8_t *option, size_t len) {
- _cleanup_dhcp6_lease_free_ sd_dhcp6_lease *lease = NULL;
+ _cleanup_(sd_dhcp6_lease_unrefp) sd_dhcp6_lease *lease = NULL;
uint8_t *optval;
uint16_t optcode;
size_t optlen;
@@ -557,7 +612,7 @@ static int test_client_verify_information_request(DHCP6Message *information_requ
while ((r = dhcp6_option_parse(&option, &len,
&optcode, &optlen, &optval)) >= 0) {
switch(optcode) {
- case DHCP6_OPTION_CLIENTID:
+ case SD_DHCP6_OPTION_CLIENTID:
assert_se(!found_clientid);
found_clientid = true;
@@ -566,17 +621,17 @@ static int test_client_verify_information_request(DHCP6Message *information_requ
break;
- case DHCP6_OPTION_IA_NA:
+ case SD_DHCP6_OPTION_IA_NA:
assert_not_reached("IA TA option must not be present");
break;
- case DHCP6_OPTION_SERVERID:
+ case SD_DHCP6_OPTION_SERVERID:
assert_not_reached("Server ID option must not be present");
break;
- case DHCP6_OPTION_ELAPSED_TIME:
+ case SD_DHCP6_OPTION_ELAPSED_TIME:
assert_se(!found_elapsed_time);
found_elapsed_time = true;
@@ -646,7 +701,8 @@ int dhcp6_network_bind_udp_socket(int index, struct in6_addr *local_address) {
static int test_client_solicit(sd_event *e) {
sd_dhcp6_client *client;
usec_t time_now = now(clock_boottime_or_monotonic());
- bool val = true;
+ struct in6_addr address = { { { 0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x01 } } };
+ int val = true;
if (verbose)
printf("* %s\n", __FUNCTION__);
@@ -656,7 +712,7 @@ static int test_client_solicit(sd_event *e) {
assert_se(sd_dhcp6_client_attach_event(client, e, 0) >= 0);
- assert_se(sd_dhcp6_client_set_index(client, test_index) == 0);
+ assert_se(sd_dhcp6_client_set_ifindex(client, test_index) == 0);
assert_se(sd_dhcp6_client_set_mac(client, (const uint8_t *) &mac_addr,
sizeof (mac_addr),
ARPHRD_ETHER) >= 0);
@@ -674,6 +730,8 @@ static int test_client_solicit(sd_event *e) {
time_now + 2 * USEC_PER_SEC, 0,
test_hangcheck, NULL) >= 0);
+ assert_se(sd_dhcp6_client_set_local_address(client, &address) >= 0);
+
assert_se(sd_dhcp6_client_start(client) >= 0);
sd_event_loop(e);
@@ -688,7 +746,7 @@ static int test_client_solicit(sd_event *e) {
}
int main(int argc, char *argv[]) {
- _cleanup_event_unref_ sd_event *e;
+ _cleanup_(sd_event_unrefp) sd_event *e;
assert_se(sd_event_new(&e) >= 0);
diff --git a/src/libsystemd-network/test-icmp6-rs.c b/src/libsystemd-network/test-icmp6-rs.c
deleted file mode 100644
index 8ba21106a7..0000000000
--- a/src/libsystemd-network/test-icmp6-rs.c
+++ /dev/null
@@ -1,357 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- This file is part of systemd.
-
- Copyright (C) 2014 Intel Corporation. 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/icmp6.h>
-
-#include "socket-util.h"
-
-#include "dhcp6-internal.h"
-#include "sd-icmp6-nd.h"
-
-static struct ether_addr mac_addr = {
- .ether_addr_octet = {'A', 'B', 'C', '1', '2', '3'}
-};
-
-static bool verbose = false;
-static sd_event_source *test_hangcheck;
-static int test_fd[2];
-
-typedef int (*send_ra_t)(uint8_t flags);
-static send_ra_t send_ra_function;
-
-static int test_rs_hangcheck(sd_event_source *s, uint64_t usec,
- void *userdata) {
- assert_se(false);
-
- return 0;
-}
-
-int dhcp_network_icmp6_bind_router_solicitation(int index) {
- assert_se(index == 42);
-
- if (socketpair(AF_UNIX, SOCK_DGRAM, 0, test_fd) < 0)
- return -errno;
-
- return test_fd[0];
-}
-
-static int send_ra_short_prefix(uint8_t flags) {
- uint8_t advertisement[] = {
- 0x86, 0x00, 0xbe, 0xd7, 0x40, 0xc0, 0x00, 0xb4,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-
- 0x03, 0x04, 0x34, 0xc0, 0x00, 0x00, 0x01, 0xf4,
- 0x00, 0x00, 0x01, 0xb8, 0x00, 0x00, 0x00, 0x00,
- 0x20, 0x01, 0x0d, 0xb8, 0xde, 0xad, 0xbe, 0xef,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
- };
-
- assert_se(write(test_fd[1], advertisement, sizeof(advertisement)) ==
- sizeof(advertisement));
-
- return 0;
-}
-
-static void test_short_prefix_cb(sd_icmp6_nd *nd, int event, void *userdata) {
- sd_event *e = userdata;
- struct {
- struct in6_addr addr;
- uint8_t prefixlen;
- bool success;
- } addrs[] = {
- { { { { 0x20, 0x01, 0x0d, 0xb8, 0xde, 0xad, 0xbe, 0xef,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } },
- 52, true },
- { { { { 0x20, 0x01, 0x0d, 0xb8, 0xde, 0xad, 0x0d, 0xad,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } },
- 64, false },
- { { { { 0x20, 0x01, 0x0d, 0xb8, 0x0b, 0x16, 0xd0, 0x0d,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } },
- 60, true },
- { { { { 0x20, 0x01, 0x0d, 0xb8, 0x00, 0x9d, 0xab, 0xcd,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } },
- 64, true },
- { { { { 0x20, 0x01, 0x0d, 0xb8, 0xde, 0xad, 0xbe, 0xed,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 } } },
- 52, true },
- };
- uint8_t prefixlen;
- unsigned int i;
-
- for (i = 0; i < ELEMENTSOF(addrs); i++) {
- printf(" %s prefix %02x%02x:%02x%02x:%02x%02x:%02x%02x",
- __FUNCTION__,
- addrs[i].addr.s6_addr[0], addrs[i].addr.s6_addr[1],
- addrs[i].addr.s6_addr[2], addrs[i].addr.s6_addr[3],
- addrs[i].addr.s6_addr[4], addrs[i].addr.s6_addr[5],
- addrs[i].addr.s6_addr[6], addrs[i].addr.s6_addr[7]);
-
- if (addrs[i].success) {
- assert_se(sd_icmp6_ra_get_prefixlen(nd, &addrs[i].addr,
- &prefixlen) >= 0);
- assert_se(addrs[i].prefixlen == prefixlen);
- printf("/%d onlink\n", prefixlen);
- } else {
- assert_se(sd_icmp6_ra_get_prefixlen(nd, &addrs[i].addr,
- &prefixlen) == -EADDRNOTAVAIL);
- printf("/128 offlink\n");
- }
- }
-
- sd_event_exit(e, 0);
-}
-
-static int send_ra_prefixes(uint8_t flags) {
- uint8_t advertisement[] = {
- 0x86, 0x00, 0xbe, 0xd7, 0x40, 0xc0, 0x00, 0xb4,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x03, 0x04, 0x3f, 0xc0, 0x00, 0x00, 0x01, 0xf4,
- 0x00, 0x00, 0x01, 0xb8, 0x00, 0x00, 0x00, 0x00,
- 0x20, 0x01, 0x0d, 0xb8, 0xde, 0xad, 0xbe, 0xef,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x03, 0x04, 0x40, 0x00, 0x00, 0x00, 0x02, 0x58,
- 0x00, 0x00, 0x02, 0x1c, 0x00, 0x00, 0x00, 0x00,
- 0x20, 0x01, 0x0d, 0xb8, 0xde, 0xad, 0x0d, 0xad,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x03, 0x04, 0x3c, 0x80, 0x00, 0x00, 0x03, 0x84,
- 0x00, 0x00, 0x03, 0x20, 0x00, 0x00, 0x00, 0x00,
- 0x20, 0x01, 0x0d, 0xb8, 0x0b, 0x16, 0xd0, 0x0d,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x03, 0x04, 0x40, 0xc0, 0x00, 0x00, 0x03, 0x84,
- 0x00, 0x00, 0x03, 0x20, 0x00, 0x00, 0x00, 0x00,
- 0x20, 0x01, 0x0d, 0xb8, 0x00, 0x9d, 0xab, 0xcd,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x19, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c,
- 0x20, 0x01, 0x0d, 0xb8, 0xde, 0xad, 0xbe, 0xef,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
- 0x1f, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c,
- 0x03, 0x6c, 0x61, 0x62, 0x05, 0x69, 0x6e, 0x74,
- 0x72, 0x61, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x01, 0x01, 0x78, 0x2b, 0xcb, 0xb3, 0x6d, 0x53
- };
-
- assert_se(write(test_fd[1], advertisement, sizeof(advertisement)) ==
- sizeof(advertisement));
-
- return 0;
-}
-
-static void test_prefixes_cb(sd_icmp6_nd *nd, int event, void *userdata) {
- sd_event *e = userdata;
- struct {
- struct in6_addr addr;
- uint8_t prefixlen;
- bool success;
- } addrs[] = {
- { { { { 0x20, 0x01, 0x0d, 0xb8, 0xde, 0xad, 0xbe, 0xef,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } },
- 63, true },
- { { { { 0x20, 0x01, 0x0d, 0xb8, 0xde, 0xad, 0x0d, 0xad,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } },
- 64, false },
- { { { { 0x20, 0x01, 0x0d, 0xb8, 0x0b, 0x16, 0xd0, 0x0d,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } },
- 60, true },
- { { { { 0x20, 0x01, 0x0d, 0xb8, 0x00, 0x9d, 0xab, 0xcd,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } },
- 64, true },
- { { { { 0x20, 0x01, 0x0d, 0xb8, 0xde, 0xad, 0xbe, 0xed,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 } } },
- 63, false },
- };
- uint8_t prefixlen;
- unsigned int i;
-
- for (i = 0; i < ELEMENTSOF(addrs); i++) {
- printf(" %s prefix %02x%02x:%02x%02x:%02x%02x:%02x%02x",
- __FUNCTION__,
- addrs[i].addr.s6_addr[0], addrs[i].addr.s6_addr[1],
- addrs[i].addr.s6_addr[2], addrs[i].addr.s6_addr[3],
- addrs[i].addr.s6_addr[4], addrs[i].addr.s6_addr[5],
- addrs[i].addr.s6_addr[6], addrs[i].addr.s6_addr[7]);
-
- if (addrs[i].success) {
- assert_se(sd_icmp6_ra_get_prefixlen(nd, &addrs[i].addr,
- &prefixlen) >= 0);
- assert_se(addrs[i].prefixlen == prefixlen);
- printf("/%d onlink\n", prefixlen);
- } else {
- assert_se(sd_icmp6_ra_get_prefixlen(nd, &addrs[i].addr,
- &prefixlen) == -EADDRNOTAVAIL);
- printf("/128 offlink\n");
- }
- }
-
- send_ra_function = send_ra_short_prefix;
- assert_se(sd_icmp6_nd_set_callback(nd, test_short_prefix_cb, e) >= 0);
- assert_se(sd_icmp6_nd_stop(nd) >= 0);
- assert_se(sd_icmp6_router_solicitation_start(nd) >= 0);
-}
-
-static void test_prefixes(void) {
- sd_event *e;
- sd_icmp6_nd *nd;
-
- if (verbose)
- printf("* %s\n", __FUNCTION__);
-
- send_ra_function = send_ra_prefixes;
-
- assert_se(sd_event_new(&e) >= 0);
-
- assert_se(sd_icmp6_nd_new(&nd) >= 0);
- assert_se(nd);
-
- assert_se(sd_icmp6_nd_attach_event(nd, e, 0) >= 0);
-
- assert_se(sd_icmp6_nd_set_index(nd, 42) >= 0);
- assert_se(sd_icmp6_nd_set_mac(nd, &mac_addr) >= 0);
- assert_se(sd_icmp6_nd_set_callback(nd, test_prefixes_cb, e) >= 0);
-
- assert_se(sd_icmp6_router_solicitation_start(nd) >= 0);
-
- sd_event_loop(e);
-
- nd = sd_icmp6_nd_unref(nd);
- assert_se(!nd);
-
- close(test_fd[1]);
-
- sd_event_unref(e);
-}
-
-static int send_ra(uint8_t flags) {
- uint8_t advertisement[] = {
- 0x86, 0x00, 0xde, 0x83, 0x40, 0xc0, 0x00, 0xb4,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x03, 0x04, 0x40, 0xc0, 0x00, 0x00, 0x01, 0xf4,
- 0x00, 0x00, 0x01, 0xb8, 0x00, 0x00, 0x00, 0x00,
- 0x20, 0x01, 0x0d, 0xb8, 0xde, 0xad, 0xbe, 0xef,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x19, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c,
- 0x20, 0x01, 0x0d, 0xb8, 0xde, 0xad, 0xbe, 0xef,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
- 0x1f, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c,
- 0x03, 0x6c, 0x61, 0x62, 0x05, 0x69, 0x6e, 0x74,
- 0x72, 0x61, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x01, 0x01, 0x78, 0x2b, 0xcb, 0xb3, 0x6d, 0x53,
- };
-
- advertisement[5] = flags;
-
- assert_se(write(test_fd[1], advertisement, sizeof(advertisement)) ==
- sizeof(advertisement));
-
- if (verbose)
- printf(" sent RA with flag 0x%02x\n", flags);
-
- return 0;
-}
-
-int dhcp_network_icmp6_send_router_solicitation(int s, const struct ether_addr *ether_addr) {
- return send_ra_function(0);
-}
-
-static void test_rs_done(sd_icmp6_nd *nd, int event, void *userdata) {
- sd_event *e = userdata;
- static int idx = 0;
- struct {
- uint8_t flag;
- int event;
- } flag_event[] = {
- { 0, ICMP6_EVENT_ROUTER_ADVERTISMENT_NONE },
- { ND_RA_FLAG_OTHER, ICMP6_EVENT_ROUTER_ADVERTISMENT_OTHER },
- { ND_RA_FLAG_MANAGED, ICMP6_EVENT_ROUTER_ADVERTISMENT_MANAGED }
- };
- uint32_t mtu;
-
- assert_se(nd);
-
- assert_se(event == flag_event[idx].event);
- idx++;
-
- if (verbose)
- printf(" got event %d\n", event);
-
- if (idx < 3) {
- send_ra(flag_event[idx].flag);
- return;
- }
-
- assert_se(sd_icmp6_ra_get_mtu(nd, &mtu) == -ENOMSG);
-
- sd_event_exit(e, 0);
-}
-
-static void test_rs(void) {
- sd_event *e;
- sd_icmp6_nd *nd;
- usec_t time_now = now(clock_boottime_or_monotonic());
-
- if (verbose)
- printf("* %s\n", __FUNCTION__);
-
- send_ra_function = send_ra;
-
- assert_se(sd_event_new(&e) >= 0);
-
- assert_se(sd_icmp6_nd_new(&nd) >= 0);
- assert_se(nd);
-
- assert_se(sd_icmp6_nd_attach_event(nd, e, 0) >= 0);
-
- assert_se(sd_icmp6_nd_set_index(nd, 42) >= 0);
- assert_se(sd_icmp6_nd_set_mac(nd, &mac_addr) >= 0);
- assert_se(sd_icmp6_nd_set_callback(nd, test_rs_done, e) >= 0);
-
- assert_se(sd_event_add_time(e, &test_hangcheck, clock_boottime_or_monotonic(),
- time_now + 2 *USEC_PER_SEC, 0,
- test_rs_hangcheck, NULL) >= 0);
-
- assert_se(sd_icmp6_nd_stop(nd) >= 0);
- assert_se(sd_icmp6_router_solicitation_start(nd) >= 0);
- assert_se(sd_icmp6_nd_stop(nd) >= 0);
-
- assert_se(sd_icmp6_router_solicitation_start(nd) >= 0);
-
- sd_event_loop(e);
-
- test_hangcheck = sd_event_source_unref(test_hangcheck);
-
- nd = sd_icmp6_nd_unref(nd);
- assert_se(!nd);
-
- close(test_fd[1]);
-
- sd_event_unref(e);
-}
-
-int main(int argc, char *argv[]) {
-
- log_set_max_level(LOG_DEBUG);
- log_parse_environment();
- log_open();
-
- test_rs();
- test_prefixes();
-
- return 0;
-}
diff --git a/src/libsystemd-network/test-ipv4ll-manual.c b/src/libsystemd-network/test-ipv4ll-manual.c
new file mode 100644
index 0000000000..2b1387fa91
--- /dev/null
+++ b/src/libsystemd-network/test-ipv4ll-manual.c
@@ -0,0 +1,128 @@
+/***
+ 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 <errno.h>
+#include <net/if.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <linux/veth.h>
+
+#include "sd-event.h"
+#include "sd-ipv4ll.h"
+#include "sd-netlink.h"
+
+#include "alloc-util.h"
+#include "in-addr-util.h"
+#include "netlink-util.h"
+#include "parse-util.h"
+#include "string-util.h"
+#include "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 SD_IPV4LL_EVENT_BIND:
+ log_info("bound %s", strna(address));
+ break;
+ case SD_IPV4LL_EVENT_CONFLICT:
+ log_info("conflict on %s", strna(address));
+ break;
+ case SD_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_ifindex(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_(sd_event_unrefp) sd_event *e = NULL;
+ _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL;
+ _cleanup_(sd_netlink_message_unrefp) 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 5677bfb2d2..fe70697075 100644
--- a/src/libsystemd-network/test-ipv4ll.c
+++ b/src/libsystemd-network/test-ipv4ll.c
@@ -1,4 +1,3 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
/***
This file is part of systemd.
@@ -18,20 +17,20 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <stdlib.h>
#include <assert.h>
#include <errno.h>
#include <stdio.h>
-#include <sys/types.h>
+#include <stdlib.h>
#include <sys/socket.h>
+#include <sys/types.h>
#include <unistd.h>
-#include "util.h"
-#include "socket-util.h"
-#include "event-util.h"
-
#include "sd-ipv4ll.h"
-#include "ipv4ll-internal.h"
+
+#include "arp-util.h"
+#include "fd-util.h"
+#include "socket-util.h"
+#include "util.h"
static bool verbose = false;
static bool extended = false;
@@ -39,15 +38,16 @@ static int test_fd[2];
static int basic_request_handler_bind = 0;
static int basic_request_handler_stop = 0;
-static void* basic_request_handler_user_data = (void*)0xCABCAB;
+static void* basic_request_handler_userdata = (void*) 0xCABCAB;
+
static void basic_request_handler(sd_ipv4ll *ll, int event, void *userdata) {
- assert_se(userdata == basic_request_handler_user_data);
+ assert_se(userdata == basic_request_handler_userdata);
switch(event) {
- case IPV4LL_EVENT_STOP:
+ case SD_IPV4LL_EVENT_STOP:
basic_request_handler_stop = 1;
break;
- case IPV4LL_EVENT_BIND:
+ case SD_IPV4LL_EVENT_BIND:
basic_request_handler_bind = 1;
break;
default:
@@ -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,55 +68,40 @@ 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) {
- uint8_t seed[8];
+ struct in_addr address = {};
+ uint64_t seed = 0;
sd_ipv4ll *ll;
struct ether_addr mac_addr = {
.ether_addr_octet = {'A', 'B', 'C', '1', '2', '3'}};
@@ -134,22 +119,31 @@ 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(ll, &address) == -EINVAL);
+ address.s_addr |= htobe32(169U << 24 | 254U << 16);
+ assert_se(sd_ipv4ll_set_address(ll, &address) == -EINVAL);
+ address.s_addr |= htobe32(0x00FF);
+ assert_se(sd_ipv4ll_set_address(ll, &address) == -EINVAL);
+ address.s_addr |= htobe32(0xF000);
+ assert_se(sd_ipv4ll_set_address(ll, &address) == 0);
+ address.s_addr |= htobe32(0x0F00);
+ assert_se(sd_ipv4ll_set_address(ll, &address) == -EINVAL);
+
+ assert_se(sd_ipv4ll_set_address_seed(NULL, seed) == -EINVAL);
assert_se(sd_ipv4ll_set_address_seed(ll, seed) == 0);
assert_se(sd_ipv4ll_set_mac(NULL, NULL) == -EINVAL);
assert_se(sd_ipv4ll_set_mac(ll, NULL) == -EINVAL);
assert_se(sd_ipv4ll_set_mac(ll, &mac_addr) == 0);
- assert_se(sd_ipv4ll_set_index(NULL, -1) == -EINVAL);
- assert_se(sd_ipv4ll_set_index(ll, -1) == -EINVAL);
- assert_se(sd_ipv4ll_set_index(ll, -99) == -EINVAL);
- assert_se(sd_ipv4ll_set_index(ll, 1) == 0);
- assert_se(sd_ipv4ll_set_index(ll, 99) == 0);
+ assert_se(sd_ipv4ll_set_ifindex(NULL, -1) == -EINVAL);
+ assert_se(sd_ipv4ll_set_ifindex(ll, -1) == -EINVAL);
+ assert_se(sd_ipv4ll_set_ifindex(ll, -99) == -EINVAL);
+ assert_se(sd_ipv4ll_set_ifindex(ll, 1) == 0);
+ assert_se(sd_ipv4ll_set_ifindex(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);
@@ -175,30 +169,29 @@ static void test_basic_request(sd_event *e) {
assert_se(sd_ipv4ll_start(ll) == -EINVAL);
assert_se(sd_ipv4ll_set_callback(ll, basic_request_handler,
- basic_request_handler_user_data) == 0);
+ basic_request_handler_userdata) == 0);
assert_se(sd_ipv4ll_start(ll) == -EINVAL);
- assert_se(sd_ipv4ll_set_index(ll, 1) == 0);
+ assert_se(sd_ipv4ll_set_ifindex(ll, 1) == 0);
assert_se(sd_ipv4ll_start(ll) == 0);
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);
+ assert_se(recv(test_fd[1], &arp, sizeof(struct ether_arp), 0) == sizeof(struct ether_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);
+ assert_se(recv(test_fd[1], &arp, sizeof(struct ether_arp), 0) == sizeof(struct ether_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);
+ assert_se(recv(test_fd[1], &arp, sizeof(struct ether_arp), 0) == sizeof(struct ether_arp));
sd_event_run(e, (uint64_t) -1);
assert_se(basic_request_handler_bind == 1);
@@ -213,13 +206,15 @@ static void test_basic_request(sd_event *e) {
}
int main(int argc, char *argv[]) {
- _cleanup_event_unref_ sd_event *e = NULL;
+ _cleanup_(sd_event_unrefp) 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/libsystemd-network/test-lldp.c b/src/libsystemd-network/test-lldp.c
index 06545aee59..6bcd65de0a 100644
--- a/src/libsystemd-network/test-lldp.c
+++ b/src/libsystemd-network/test-lldp.c
@@ -1,5 +1,3 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
/***
This file is part of systemd.
@@ -20,212 +18,244 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
+#include <arpa/inet.h>
+#include <net/ethernet.h>
#include <stdio.h>
#include <string.h>
-#include <net/ethernet.h>
-#include <arpa/inet.h>
+#include <unistd.h>
+
+#include "sd-event.h"
+#include "sd-lldp.h"
+#include "alloc-util.h"
+#include "fd-util.h"
+#include "lldp-network.h"
#include "macro.h"
-#include "lldp.h"
-#include "lldp-tlv.h"
+#include "string-util.h"
#define TEST_LLDP_PORT "em1"
#define TEST_LLDP_TYPE_SYSTEM_NAME "systemd-lldp"
#define TEST_LLDP_TYPE_SYSTEM_DESC "systemd-lldp-desc"
-static struct ether_addr mac_addr = {
- .ether_addr_octet = {'A', 'B', 'C', '1', '2', '3'}
-};
-
-static int lldp_build_tlv_packet(tlv_packet **ret) {
- _cleanup_tlv_packet_free_ tlv_packet *m = NULL;
- const uint8_t lldp_dst[] = LLDP_MULTICAST_ADDR;
- struct ether_header ether = {
- .ether_type = htons(ETHERTYPE_LLDP),
- };
-
- /* Append ethernet header */
- memcpy(&ether.ether_dhost, lldp_dst, ETHER_ADDR_LEN);
- memcpy(&ether.ether_shost, &mac_addr, ETHER_ADDR_LEN);
-
- assert_se(tlv_packet_new(&m) >= 0);
-
- assert_se(tlv_packet_append_bytes(m, &ether, sizeof(struct ether_header)) >= 0);
-
- assert_se(lldp_tlv_packet_open_container(m, LLDP_TYPE_CHASSIS_ID) >= 0);
-
- assert_se(tlv_packet_append_u8(m, LLDP_CHASSIS_SUBTYPE_MAC_ADDRESS) >= 0);
- assert_se(tlv_packet_append_bytes(m, &mac_addr, ETHER_ADDR_LEN) >= 0);
-
- assert_se(lldp_tlv_packet_close_container(m) >= 0);
-
- /* port name */
- assert_se(lldp_tlv_packet_open_container(m, LLDP_TYPE_PORT_ID) >= 0);
-
- assert_se(tlv_packet_append_u8(m, LLDP_PORT_SUBTYPE_INTERFACE_NAME) >= 0);
- assert_se(tlv_packet_append_bytes(m, TEST_LLDP_PORT, strlen(TEST_LLDP_PORT) + 1) >= 0);
-
- assert_se(lldp_tlv_packet_close_container(m) >= 0);
-
- /* ttl */
- assert_se(lldp_tlv_packet_open_container(m, LLDP_TYPE_TTL) >= 0);
-
- assert_se(tlv_packet_append_u16(m, 170) >= 0);
-
- assert_se(lldp_tlv_packet_close_container(m) >= 0);
-
- /* system name */
- assert_se(lldp_tlv_packet_open_container(m, LLDP_TYPE_SYSTEM_NAME) >= 0);
-
- assert_se(tlv_packet_append_bytes(m, TEST_LLDP_TYPE_SYSTEM_NAME,
- strlen(TEST_LLDP_TYPE_SYSTEM_NAME)) >= 0);
- assert_se(lldp_tlv_packet_close_container(m) >= 0);
+static int test_fd[2] = { -1, -1 };
+static int lldp_handler_calls;
- /* system descrition */
- assert_se(lldp_tlv_packet_open_container(m, LLDP_TYPE_SYSTEM_DESCRIPTION) >= 0);
+int lldp_network_bind_raw_socket(int ifindex) {
+ if (socketpair(AF_UNIX, SOCK_DGRAM | SOCK_NONBLOCK, 0, test_fd) < 0)
+ return -errno;
- assert_se(tlv_packet_append_bytes(m, TEST_LLDP_TYPE_SYSTEM_DESC,
- strlen(TEST_LLDP_TYPE_SYSTEM_DESC)) >= 0);
-
- assert_se(lldp_tlv_packet_close_container(m) >= 0);
-
- /* Mark end of packet */
- assert_se(lldp_tlv_packet_open_container(m, LLDP_TYPE_END) >= 0);
- assert_se(lldp_tlv_packet_close_container(m) >= 0);
-
- *ret = m;
-
- m = NULL;
-
- return 0;
+ return test_fd[0];
}
-static int lldp_parse_chassis_tlv(tlv_packet *m, uint8_t *type) {
- uint8_t *p, subtype;
- uint16_t length;
+static void lldp_handler(sd_lldp *lldp, sd_lldp_event event, sd_lldp_neighbor *n, void *userdata) {
+ lldp_handler_calls++;
+}
- assert_se(lldp_tlv_packet_enter_container(m, LLDP_TYPE_CHASSIS_ID) >= 0);
- assert_se(tlv_packet_read_u8(m, &subtype) >= 0);
+static int start_lldp(sd_lldp **lldp, sd_event *e, sd_lldp_callback_t cb, void *cb_data) {
+ int r;
- switch (subtype) {
- case LLDP_CHASSIS_SUBTYPE_MAC_ADDRESS:
+ r = sd_lldp_new(lldp);
+ if (r < 0)
+ return r;
- *type = LLDP_CHASSIS_SUBTYPE_MAC_ADDRESS;
- assert_se(tlv_packet_read_bytes(m, &p, &length) >= 0);
+ r = sd_lldp_set_ifindex(*lldp, 42);
+ if (r < 0)
+ return r;
- assert_se(memcmp(p, &mac_addr.ether_addr_octet, ETHER_ADDR_LEN) == 0);
+ r = sd_lldp_set_callback(*lldp, cb, cb_data);
+ if (r < 0)
+ return r;
- break;
- default:
- assert_not_reached("Unhandled option");
- }
+ r = sd_lldp_attach_event(*lldp, e, 0);
+ if (r < 0)
+ return r;
- assert_se(lldp_tlv_packet_exit_container(m) >= 0);
+ r = sd_lldp_start(*lldp);
+ if (r < 0)
+ return r;
return 0;
}
-static int lldp_parse_port_id_tlv(tlv_packet *m) {
- _cleanup_free_ char *p = NULL;
- char *str = NULL;
- uint16_t length;
- uint8_t subtype;
-
- assert_se(lldp_tlv_packet_enter_container(m, LLDP_TYPE_PORT_ID) >= 0);
-
- assert_se(tlv_packet_read_u8(m, &subtype) >= 0);
+static int stop_lldp(sd_lldp *lldp) {
+ int r;
- switch (subtype) {
- case LLDP_PORT_SUBTYPE_INTERFACE_NAME:
- assert_se(tlv_packet_read_string(m, &str, &length) >= 0);
+ r = sd_lldp_stop(lldp);
+ if (r < 0)
+ return r;
- p = strndup(str, length-1);
- assert_se(p);
+ r = sd_lldp_detach_event(lldp);
+ if (r < 0)
+ return r;
- assert_se(streq(p, TEST_LLDP_PORT) == 1);
- break;
- default:
- assert_not_reached("Unhandled option");
- }
-
- assert_se(lldp_tlv_packet_exit_container(m) >= 0);
+ sd_lldp_unref(lldp);
+ safe_close(test_fd[1]);
return 0;
}
-static int lldp_parse_system_name_tlv(tlv_packet *m) {
- _cleanup_free_ char *p = NULL;
- char *str = NULL;
- uint16_t length;
+static void test_receive_basic_packet(sd_event *e) {
+
+ static const uint8_t frame[] = {
+ /* Ethernet header */
+ 0x01, 0x80, 0xc2, 0x00, 0x00, 0x03, /* Destination MAC*/
+ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, /* Source MAC */
+ 0x88, 0xcc, /* Ethertype */
+ /* LLDP mandatory TLVs */
+ 0x02, 0x07, 0x04, 0x00, 0x01, 0x02, /* Chassis: MAC, 00:01:02:03:04:05 */
+ 0x03, 0x04, 0x05,
+ 0x04, 0x04, 0x05, 0x31, 0x2f, 0x33, /* Port: interface name, "1/3" */
+ 0x06, 0x02, 0x00, 0x78, /* TTL: 120 seconds*/
+ /* LLDP optional TLVs */
+ 0x08, 0x04, 0x50, 0x6f, 0x72, 0x74, /* Port Description: "Port" */
+ 0x0a, 0x03, 0x53, 0x59, 0x53, /* System Name: "SYS" */
+ 0x0c, 0x04, 0x66, 0x6f, 0x6f, 0x00, /* System Description: "foo" (NULL-terminated) */
+ 0x00, 0x00 /* End Of LLDPDU */
+ };
- assert_se(lldp_tlv_packet_enter_container(m, LLDP_TYPE_SYSTEM_NAME) >= 0);
- assert_se(tlv_packet_read_string(m, &str, &length) >= 0);
+ sd_lldp *lldp;
+ sd_lldp_neighbor **neighbors;
+ uint8_t type;
+ const void *data;
+ uint16_t ttl;
+ size_t length;
+ const char *str;
- p = strndup(str, length);
- assert_se(p);
+ lldp_handler_calls = 0;
+ assert_se(start_lldp(&lldp, e, lldp_handler, NULL) == 0);
- assert_se(streq(p, TEST_LLDP_TYPE_SYSTEM_NAME) == 1);
+ assert_se(write(test_fd[1], frame, sizeof(frame)) == sizeof(frame));
+ sd_event_run(e, 0);
+ assert_se(lldp_handler_calls == 1);
+ assert_se(sd_lldp_get_neighbors(lldp, &neighbors) == 1);
- assert_se(lldp_tlv_packet_exit_container(m) >= 0);
+ assert_se(sd_lldp_neighbor_get_chassis_id(neighbors[0], &type, &data, &length) == 0);
+ assert_se(type == SD_LLDP_CHASSIS_SUBTYPE_MAC_ADDRESS);
+ assert_se(length == ETH_ALEN);
+ assert_se(!memcmp(data, "\x00\x01\x02\x03\x04\x05", ETH_ALEN));
- return 1;
-}
+ assert_se(sd_lldp_neighbor_get_port_id(neighbors[0], &type, &data, &length) == 0);
+ assert_se(type == SD_LLDP_PORT_SUBTYPE_INTERFACE_NAME);
+ assert_se(length == 3);
+ assert_se(strneq((char *) data, "1/3", 3));
-static int lldp_parse_system_desc_tlv(tlv_packet *m) {
- _cleanup_free_ char *p = NULL;
- char *str = NULL;
- uint16_t length;
+ assert_se(sd_lldp_neighbor_get_port_description(neighbors[0], &str) == 0);
+ assert_se(streq(str, "Port"));
- assert_se(lldp_tlv_packet_enter_container(m, LLDP_TYPE_SYSTEM_DESCRIPTION) >= 0);
- assert_se(tlv_packet_read_string(m, &str, &length) >= 0);
+ assert_se(sd_lldp_neighbor_get_system_name(neighbors[0], &str) == 0);
+ assert_se(streq(str, "SYS"));
- p = strndup(str, length);
- assert_se(p);
+ assert_se(sd_lldp_neighbor_get_system_description(neighbors[0], &str) == 0);
+ assert_se(streq(str, "foo"));
- assert_se(streq(p, TEST_LLDP_TYPE_SYSTEM_DESC) == 1);
+ assert_se(sd_lldp_neighbor_get_ttl(neighbors[0], &ttl) == 0);
+ assert_se(ttl == 120);
- assert_se(lldp_tlv_packet_exit_container(m) >= 0);
+ sd_lldp_neighbor_unref(neighbors[0]);
+ free(neighbors);
- return 0;
+ assert_se(stop_lldp(lldp) == 0);
}
-static int lldp_parse_ttl_tlv(tlv_packet *m) {
- uint16_t ttl;
-
- assert_se(lldp_tlv_packet_enter_container(m, LLDP_TYPE_TTL) >= 0);
- assert_se(tlv_packet_read_u16(m, &ttl) >= 0);
+static void test_receive_incomplete_packet(sd_event *e) {
+ sd_lldp *lldp;
+ sd_lldp_neighbor **neighbors;
+ uint8_t frame[] = {
+ /* Ethernet header */
+ 0x01, 0x80, 0xc2, 0x00, 0x00, 0x03, /* Destination MAC*/
+ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, /* Source MAC */
+ 0x88, 0xcc, /* Ethertype */
+ /* LLDP mandatory TLVs */
+ 0x02, 0x07, 0x04, 0x00, 0x01, 0x02, /* Chassis: MAC, 00:01:02:03:04:05 */
+ 0x03, 0x04, 0x05,
+ 0x04, 0x04, 0x05, 0x31, 0x2f, 0x33, /* Port: interface name, "1/3" */
+ /* Missing TTL */
+ 0x00, 0x00 /* End Of LLDPDU */
+ };
- assert_se(ttl == 170);
+ lldp_handler_calls = 0;
+ assert_se(start_lldp(&lldp, e, lldp_handler, NULL) == 0);
- assert_se(lldp_tlv_packet_exit_container(m) >= 0);
+ assert_se(write(test_fd[1], frame, sizeof(frame)) == sizeof(frame));
+ sd_event_run(e, 0);
+ assert_se(lldp_handler_calls == 0);
+ assert_se(sd_lldp_get_neighbors(lldp, &neighbors) == 0);
- return 0;
+ assert_se(stop_lldp(lldp) == 0);
}
-static int lldp_parse_tlv_packet(tlv_packet *m, int len) {
- uint8_t subtype;
-
- assert_se(tlv_packet_parse_pdu(m, len) >= 0);
- assert_se(lldp_parse_chassis_tlv(m, &subtype) >= 0);
- assert_se(lldp_parse_port_id_tlv(m) >= 0);
- assert_se(lldp_parse_system_name_tlv(m) >= 0);
- assert_se(lldp_parse_ttl_tlv(m) >= 0);
- assert_se(lldp_parse_system_desc_tlv(m) >= 0);
+static void test_receive_oui_packet(sd_event *e) {
+ sd_lldp *lldp;
+ sd_lldp_neighbor **neighbors;
+ uint8_t frame[] = {
+ /* Ethernet header */
+ 0x01, 0x80, 0xc2, 0x00, 0x00, 0x03, /* Destination MAC*/
+ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, /* Source MAC */
+ 0x88, 0xcc, /* Ethertype */
+ /* LLDP mandatory TLVs */
+ 0x02, 0x07, 0x04, 0x00, 0x01, 0x02, /* Chassis: MAC, 00:01:02:03:04:05 */
+ 0x03, 0x04, 0x05,
+ 0x04, 0x04, 0x05, 0x31, 0x2f, 0x33, /* Port TLV: interface name, "1/3" */
+ 0x06, 0x02, 0x00, 0x78, /* TTL: 120 seconds*/
+ /* LLDP optional TLVs */
+ 0xfe, 0x06, 0x00, 0x80, 0xc2, 0x01, /* Port VLAN ID: 0x1234 */
+ 0x12, 0x34,
+ 0xfe, 0x07, 0x00, 0x80, 0xc2, 0x02, /* Port and protocol: flag 1, PPVID 0x7788 */
+ 0x01, 0x77, 0x88,
+ 0xfe, 0x0d, 0x00, 0x80, 0xc2, 0x03, /* VLAN Name: ID 0x1234, name "Vlan51" */
+ 0x12, 0x34, 0x06, 0x56, 0x6c, 0x61,
+ 0x6e, 0x35, 0x31,
+ 0xfe, 0x06, 0x00, 0x80, 0xc2, 0x06, /* Management VID: 0x0102 */
+ 0x01, 0x02,
+ 0xfe, 0x09, 0x00, 0x80, 0xc2, 0x07, /* Link aggregation: status 1, ID 0x00140012 */
+ 0x01, 0x00, 0x14, 0x00, 0x12,
+ 0x00, 0x00 /* End of LLDPDU */
+ };
- return 0;
+ lldp_handler_calls = 0;
+ assert_se(start_lldp(&lldp, e, lldp_handler, NULL) == 0);
+
+ assert_se(write(test_fd[1], frame, sizeof(frame)) == sizeof(frame));
+ sd_event_run(e, 0);
+ assert_se(lldp_handler_calls == 1);
+ assert_se(sd_lldp_get_neighbors(lldp, &neighbors) == 1);
+
+ assert_se(sd_lldp_neighbor_tlv_rewind(neighbors[0]) >= 0);
+ assert_se(sd_lldp_neighbor_tlv_is_type(neighbors[0], SD_LLDP_TYPE_CHASSIS_ID) > 0);
+ assert_se(sd_lldp_neighbor_tlv_next(neighbors[0]) > 0);
+ assert_se(sd_lldp_neighbor_tlv_is_type(neighbors[0], SD_LLDP_TYPE_PORT_ID) > 0);
+ assert_se(sd_lldp_neighbor_tlv_next(neighbors[0]) > 0);
+ assert_se(sd_lldp_neighbor_tlv_is_type(neighbors[0], SD_LLDP_TYPE_TTL) > 0);
+ assert_se(sd_lldp_neighbor_tlv_next(neighbors[0]) > 0);
+ assert_se(sd_lldp_neighbor_tlv_is_oui(neighbors[0], SD_LLDP_OUI_802_1, SD_LLDP_OUI_802_1_SUBTYPE_PORT_VLAN_ID) > 0);
+ assert_se(sd_lldp_neighbor_tlv_next(neighbors[0]) > 0);
+ assert_se(sd_lldp_neighbor_tlv_is_oui(neighbors[0], SD_LLDP_OUI_802_1, SD_LLDP_OUI_802_1_SUBTYPE_PORT_PROTOCOL_VLAN_ID) > 0);
+ assert_se(sd_lldp_neighbor_tlv_next(neighbors[0]) > 0);
+ assert_se(sd_lldp_neighbor_tlv_is_oui(neighbors[0], SD_LLDP_OUI_802_1, SD_LLDP_OUI_802_1_SUBTYPE_VLAN_NAME) > 0);
+ assert_se(sd_lldp_neighbor_tlv_next(neighbors[0]) > 0);
+ assert_se(sd_lldp_neighbor_tlv_is_oui(neighbors[0], SD_LLDP_OUI_802_1, SD_LLDP_OUI_802_1_SUBTYPE_MANAGEMENT_VID) > 0);
+ assert_se(sd_lldp_neighbor_tlv_next(neighbors[0]) > 0);
+ assert_se(sd_lldp_neighbor_tlv_is_oui(neighbors[0], SD_LLDP_OUI_802_1, SD_LLDP_OUI_802_1_SUBTYPE_LINK_AGGREGATION) > 0);
+ assert_se(sd_lldp_neighbor_tlv_next(neighbors[0]) > 0);
+ assert_se(sd_lldp_neighbor_tlv_is_type(neighbors[0], SD_LLDP_TYPE_END) > 0);
+ assert_se(sd_lldp_neighbor_tlv_next(neighbors[0]) == 0);
+
+ sd_lldp_neighbor_unref(neighbors[0]);
+ free(neighbors);
+
+ assert_se(stop_lldp(lldp) == 0);
}
int main(int argc, char *argv[]) {
- _cleanup_tlv_packet_free_ tlv_packet *tlv = NULL;
-
- /* form a packet */
- lldp_build_tlv_packet(&tlv);
+ _cleanup_(sd_event_unrefp) sd_event *e = NULL;
- /* parse the packet */
- tlv_packet_parse_pdu(tlv, tlv->length);
+ log_set_max_level(LOG_DEBUG);
- /* verify */
- lldp_parse_tlv_packet(tlv, tlv->length);
+ /* LLDP reception tests */
+ assert_se(sd_event_new(&e) == 0);
+ test_receive_basic_packet(e);
+ test_receive_incomplete_packet(e);
+ test_receive_oui_packet(e);
return 0;
}
diff --git a/src/libsystemd-network/test-ndisc-rs.c b/src/libsystemd-network/test-ndisc-rs.c
new file mode 100644
index 0000000000..d9669488be
--- /dev/null
+++ b/src/libsystemd-network/test-ndisc-rs.c
@@ -0,0 +1,317 @@
+/***
+ This file is part of systemd.
+
+ Copyright (C) 2014 Intel Corporation. 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/icmp6.h>
+#include <arpa/inet.h>
+
+#include "sd-ndisc.h"
+
+#include "alloc-util.h"
+#include "hexdecoct.h"
+#include "icmp6-util.h"
+#include "socket-util.h"
+#include "strv.h"
+
+static struct ether_addr mac_addr = {
+ .ether_addr_octet = {'A', 'B', 'C', '1', '2', '3'}
+};
+
+static bool verbose = false;
+static sd_event_source *test_hangcheck;
+static int test_fd[2];
+
+typedef int (*send_ra_t)(uint8_t flags);
+static send_ra_t send_ra_function;
+
+static void router_dump(sd_ndisc_router *rt) {
+ struct in6_addr addr;
+ char buf[FORMAT_TIMESTAMP_MAX];
+ uint8_t hop_limit;
+ uint64_t t, flags;
+ uint32_t mtu;
+ uint16_t lifetime;
+ unsigned preference;
+ int r;
+
+ assert_se(rt);
+
+ log_info("--");
+ assert_se(sd_ndisc_router_get_address(rt, &addr) == -ENODATA);
+
+ assert_se(sd_ndisc_router_get_timestamp(rt, CLOCK_REALTIME, &t) >= 0);
+ log_info("Timestamp: %s", format_timestamp(buf, sizeof(buf), t));
+
+ assert_se(sd_ndisc_router_get_timestamp(rt, CLOCK_MONOTONIC, &t) >= 0);
+ log_info("Monotonic: %" PRIu64, t);
+
+ if (sd_ndisc_router_get_hop_limit(rt, &hop_limit) < 0)
+ log_info("No hop limit set");
+ else
+ log_info("Hop limit: %u", hop_limit);
+
+ assert_se(sd_ndisc_router_get_flags(rt, &flags) >= 0);
+ log_info("Flags: <%s|%s>",
+ flags & ND_RA_FLAG_OTHER ? "OTHER" : "",
+ flags & ND_RA_FLAG_MANAGED ? "MANAGED" : "");
+
+ assert_se(sd_ndisc_router_get_preference(rt, &preference) >= 0);
+ log_info("Preference: %s",
+ preference == SD_NDISC_PREFERENCE_LOW ? "low" :
+ preference == SD_NDISC_PREFERENCE_HIGH ? "high" : "medium");
+
+ assert_se(sd_ndisc_router_get_lifetime(rt, &lifetime) >= 0);
+ log_info("Lifetime: %" PRIu16, lifetime);
+
+ if (sd_ndisc_router_get_mtu(rt, &mtu) < 0)
+ log_info("No MTU set");
+ else
+ log_info("MTU: %" PRIu32, mtu);
+
+ r = sd_ndisc_router_option_rewind(rt);
+ for (;;) {
+ uint8_t type;
+
+ assert_se(r >= 0);
+
+ if (r == 0)
+ break;
+
+ assert_se(sd_ndisc_router_option_get_type(rt, &type) >= 0);
+
+ log_info(">> Option %u", type);
+
+ switch (type) {
+
+ case SD_NDISC_OPTION_SOURCE_LL_ADDRESS:
+ case SD_NDISC_OPTION_TARGET_LL_ADDRESS: {
+ _cleanup_free_ char *c = NULL;
+ const void *p;
+ size_t n;
+
+ assert_se(sd_ndisc_router_option_get_raw(rt, &p, &n) >= 0);
+ assert_se(n > 2);
+ assert_se(c = hexmem((uint8_t*) p + 2, n - 2));
+
+ log_info("Address: %s", c);
+ break;
+ }
+
+ case SD_NDISC_OPTION_PREFIX_INFORMATION: {
+ uint32_t lifetime_valid, lifetime_preferred;
+ unsigned prefix_len;
+ uint8_t pfl;
+ struct in6_addr a;
+ char buff[INET6_ADDRSTRLEN];
+
+ assert_se(sd_ndisc_router_prefix_get_valid_lifetime(rt, &lifetime_valid) >= 0);
+ log_info("Valid Lifetime: %" PRIu32, lifetime_valid);
+
+ assert_se(sd_ndisc_router_prefix_get_preferred_lifetime(rt, &lifetime_preferred) >= 0);
+ log_info("Preferred Lifetime: %" PRIu32, lifetime_preferred);
+
+ assert_se(sd_ndisc_router_prefix_get_flags(rt, &pfl) >= 0);
+ log_info("Flags: <%s|%s>",
+ pfl & ND_OPT_PI_FLAG_ONLINK ? "ONLINK" : "",
+ pfl & ND_OPT_PI_FLAG_AUTO ? "AUTO" : "");
+
+ assert_se(sd_ndisc_router_prefix_get_prefixlen(rt, &prefix_len) >= 0);
+ log_info("Prefix Length: %u", prefix_len);
+
+ assert_se(sd_ndisc_router_prefix_get_address(rt, &a) >= 0);
+ log_info("Prefix: %s", inet_ntop(AF_INET6, &a, buff, sizeof(buff)));
+
+ break;
+ }
+
+ case SD_NDISC_OPTION_RDNSS: {
+ const struct in6_addr *a;
+ uint32_t lt;
+ int n, i;
+
+ n = sd_ndisc_router_rdnss_get_addresses(rt, &a);
+ assert_se(n > 0);
+
+ for (i = 0; i < n; i++) {
+ char buff[INET6_ADDRSTRLEN];
+ log_info("DNS: %s", inet_ntop(AF_INET6, a + i, buff, sizeof(buff)));
+ }
+
+ assert_se(sd_ndisc_router_rdnss_get_lifetime(rt, &lt) >= 0);
+ log_info("Lifetime: %" PRIu32, lt);
+ break;
+ }
+
+ case SD_NDISC_OPTION_DNSSL: {
+ _cleanup_strv_free_ char **l = NULL;
+ uint32_t lt;
+ int n, i;
+
+ n = sd_ndisc_router_dnssl_get_domains(rt, &l);
+ assert_se(n > 0);
+
+ for (i = 0; i < n; i++)
+ log_info("Domain: %s", l[i]);
+
+ assert_se(sd_ndisc_router_dnssl_get_lifetime(rt, &lt) >= 0);
+ log_info("Lifetime: %" PRIu32, lt);
+ break;
+ }}
+
+ r = sd_ndisc_router_option_next(rt);
+ }
+}
+
+static int test_rs_hangcheck(sd_event_source *s, uint64_t usec,
+ void *userdata) {
+ assert_se(false);
+
+ return 0;
+}
+
+int icmp6_bind_router_solicitation(int index) {
+ assert_se(index == 42);
+
+ if (socketpair(AF_UNIX, SOCK_DGRAM, 0, test_fd) < 0)
+ return -errno;
+
+ return test_fd[0];
+}
+
+static int send_ra(uint8_t flags) {
+ uint8_t advertisement[] = {
+ 0x86, 0x00, 0xde, 0x83, 0x40, 0xc0, 0x00, 0xb4,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x03, 0x04, 0x40, 0xc0, 0x00, 0x00, 0x01, 0xf4,
+ 0x00, 0x00, 0x01, 0xb8, 0x00, 0x00, 0x00, 0x00,
+ 0x20, 0x01, 0x0d, 0xb8, 0xde, 0xad, 0xbe, 0xef,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x19, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c,
+ 0x20, 0x01, 0x0d, 0xb8, 0xde, 0xad, 0xbe, 0xef,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+ 0x1f, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c,
+ 0x03, 0x6c, 0x61, 0x62, 0x05, 0x69, 0x6e, 0x74,
+ 0x72, 0x61, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x01, 0x78, 0x2b, 0xcb, 0xb3, 0x6d, 0x53,
+ };
+
+ advertisement[5] = flags;
+
+ assert_se(write(test_fd[1], advertisement, sizeof(advertisement)) ==
+ sizeof(advertisement));
+
+ if (verbose)
+ printf(" sent RA with flag 0x%02x\n", flags);
+
+ return 0;
+}
+
+int icmp6_send_router_solicitation(int s, const struct ether_addr *ether_addr) {
+ return send_ra_function(0);
+}
+
+static void test_callback(sd_ndisc *nd, sd_ndisc_event event, sd_ndisc_router *rt, void *userdata) {
+ sd_event *e = userdata;
+ static unsigned idx = 0;
+ uint64_t flags_array[] = {
+ 0,
+ 0,
+ 0,
+ ND_RA_FLAG_OTHER,
+ ND_RA_FLAG_MANAGED
+ };
+ uint64_t flags;
+ uint32_t mtu;
+
+ assert_se(nd);
+
+ if (event != SD_NDISC_EVENT_ROUTER)
+ return;
+
+ router_dump(rt);
+
+ assert_se(sd_ndisc_router_get_flags(rt, &flags) >= 0);
+ assert_se(flags == flags_array[idx]);
+ idx++;
+
+ if (verbose)
+ printf(" got event 0x%02" PRIx64 "\n", flags);
+
+ if (idx < ELEMENTSOF(flags_array)) {
+ send_ra(flags_array[idx]);
+ return;
+ }
+
+ assert_se(sd_ndisc_get_mtu(nd, &mtu) == -ENODATA);
+
+ sd_event_exit(e, 0);
+}
+
+static void test_rs(void) {
+ sd_event *e;
+ sd_ndisc *nd;
+ usec_t time_now = now(clock_boottime_or_monotonic());
+
+ if (verbose)
+ printf("* %s\n", __FUNCTION__);
+
+ send_ra_function = send_ra;
+
+ assert_se(sd_event_new(&e) >= 0);
+
+ assert_se(sd_ndisc_new(&nd) >= 0);
+ assert_se(nd);
+
+ assert_se(sd_ndisc_attach_event(nd, e, 0) >= 0);
+
+ assert_se(sd_ndisc_set_ifindex(nd, 42) >= 0);
+ assert_se(sd_ndisc_set_mac(nd, &mac_addr) >= 0);
+ assert_se(sd_ndisc_set_callback(nd, test_callback, e) >= 0);
+
+ assert_se(sd_event_add_time(e, &test_hangcheck, clock_boottime_or_monotonic(),
+ time_now + 2 *USEC_PER_SEC, 0,
+ test_rs_hangcheck, NULL) >= 0);
+
+ assert_se(sd_ndisc_stop(nd) >= 0);
+ assert_se(sd_ndisc_start(nd) >= 0);
+ assert_se(sd_ndisc_stop(nd) >= 0);
+
+ assert_se(sd_ndisc_start(nd) >= 0);
+
+ sd_event_loop(e);
+
+ test_hangcheck = sd_event_source_unref(test_hangcheck);
+
+ nd = sd_ndisc_unref(nd);
+ assert_se(!nd);
+
+ close(test_fd[1]);
+
+ sd_event_unref(e);
+}
+
+int main(int argc, char *argv[]) {
+
+ log_set_max_level(LOG_DEBUG);
+ log_parse_environment();
+ log_open();
+
+ test_rs();
+
+ return 0;
+}
diff --git a/src/libsystemd-network/test-pppoe.c b/src/libsystemd-network/test-pppoe.c
deleted file mode 100644
index 72878f4b51..0000000000
--- a/src/libsystemd-network/test-pppoe.c
+++ /dev/null
@@ -1,176 +0,0 @@
-/*-*- 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 "util.h"
-#include "sd-event.h"
-#include "event-util.h"
-#include "sd-netlink.h"
-#include "sd-pppoe.h"
-#include "process-util.h"
-
-static void pppoe_handler(sd_pppoe *ppp, int event, void *userdata) {
- static int pppoe_state = -1;
- sd_event *e = userdata;
-
- assert_se(ppp);
- assert_se(e);
-
- switch (event) {
- case PPPOE_EVENT_RUNNING:
- assert_se(pppoe_state == -1);
- log_info("running");
- break;
- case PPPOE_EVENT_STOPPED:
- assert_se(pppoe_state == PPPOE_EVENT_RUNNING);
- log_info("stopped");
- assert_se(sd_event_exit(e, 0) >= 0);
- break;
- default:
- assert_not_reached("invalid pppoe event");
- }
-
- pppoe_state = event;
-}
-
-static int client_run(const char *client_name, sd_event *e) {
- sd_pppoe *pppoe;
- int client_ifindex;
-
- client_ifindex = (int) if_nametoindex(client_name);
- assert_se(client_ifindex > 0);
-
- assert_se(sd_pppoe_new(&pppoe) >= 0);
- assert_se(sd_pppoe_attach_event(pppoe, e, 0) >= 0);
-
- assert_se(sd_pppoe_set_ifname(pppoe, "pppoe-client") >= 0);
- assert_se(sd_pppoe_set_ifindex(pppoe, client_ifindex) >= 0);
- assert_se(sd_pppoe_set_callback(pppoe, pppoe_handler, e) >= 0);
-
- log_info("starting PPPoE client, it will exit when the server times out and sends PADT");
-
- assert_se(sd_pppoe_start(pppoe) >= 0);
-
- assert_se(sd_event_loop(e) >= 0);
-
- assert_se(!sd_pppoe_unref(pppoe));
-
- return EXIT_SUCCESS;
-}
-
-static int test_pppoe_server(sd_event *e) {
- sd_netlink *rtnl;
- sd_netlink_message *m;
- pid_t pid;
- int r, client_ifindex, server_ifindex;
-
- r = unshare(CLONE_NEWNET);
- if (r < 0 && errno == EPERM)
- return EXIT_TEST_SKIP;
-
- assert_se(r >= 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_NEWLINK, 0) >= 0);
- assert_se(sd_netlink_message_append_string(m, IFLA_IFNAME, "pppoe-server") >= 0);
- assert_se(sd_netlink_message_open_container(m, IFLA_LINKINFO) >= 0);
- assert_se(sd_netlink_message_open_container_union(m, IFLA_INFO_DATA, "veth") >= 0);
- assert_se(sd_netlink_message_open_container(m, VETH_INFO_PEER) >= 0);
- assert_se(sd_netlink_message_append_string(m, IFLA_IFNAME, "pppoe-client") >= 0);
- assert_se(sd_netlink_message_close_container(m) >= 0);
- assert_se(sd_netlink_message_close_container(m) >= 0);
- assert_se(sd_netlink_message_close_container(m) >= 0);
- assert_se(sd_netlink_call(rtnl, m, 0, NULL) >= 0);
-
- client_ifindex = (int) if_nametoindex("pppoe-client");
- assert_se(client_ifindex > 0);
- server_ifindex = (int) if_nametoindex("pppoe-server");
- assert_se(server_ifindex > 0);
-
- m = sd_netlink_message_unref(m);
- assert_se(sd_rtnl_message_new_link(rtnl, &m, RTM_SETLINK, client_ifindex) >= 0);
- assert_se(sd_rtnl_message_link_set_flags(m, IFF_UP, IFF_UP) >= 0);
- assert_se(sd_netlink_call(rtnl, m, 0, NULL) >= 0);
-
- m = sd_netlink_message_unref(m);
- assert_se(sd_rtnl_message_new_link(rtnl, &m, RTM_SETLINK, server_ifindex) >= 0);
- assert_se(sd_rtnl_message_link_set_flags(m, IFF_UP, IFF_UP) >= 0);
- assert_se(sd_netlink_call(rtnl, m, 0, NULL) >= 0);
-
- pid = fork();
- assert_se(pid >= 0);
- if (pid == 0) {
- /* let the client send some discover messages before the server is started */
- sleep(2);
-
- /* TODO: manage pppoe-server-options */
- execlp("pppoe-server", "pppoe-server", "-F",
- "-I", "pppoe-server",
- "-C", "Test-AC",
- "-S", "Service-Default",
- "-S", "Service-First-Auxiliary",
- "-S", "Service-Second-Auxiliary",
- NULL);
- assert_not_reached("failed to execute pppoe-server. not installed?");
- }
-
- client_run("pppoe-client", e);
-
- assert_se(kill(pid, SIGTERM) >= 0);
- assert_se(wait_for_terminate(pid, NULL) >= 0);
-
- assert_se(!sd_netlink_message_unref(m));
- assert_se(!sd_netlink_unref(rtnl));
-
- return EXIT_SUCCESS;
-}
-
-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);
-
- if (argc == 1) {
- log_info("running PPPoE client against local server");
-
- return test_pppoe_server(e);
- } else if (argc == 2) {
- log_info("running PPPoE client over '%s'", argv[1]);
-
- return client_run(argv[1], e);
- } else {
- log_error("This program takes one or no arguments.\n"
- "\t %s [<ifname>]", program_invocation_short_name);
- return EXIT_FAILURE;
- }
-}