diff options
Diffstat (limited to 'src/libsystemd-network')
33 files changed, 1996 insertions, 1084 deletions
diff --git a/src/libsystemd-network/arp-util.c b/src/libsystemd-network/arp-util.c new file mode 100644 index 0000000000..2f5b9b3731 --- /dev/null +++ b/src/libsystemd-network/arp-util.c @@ -0,0 +1,153 @@ +/*** + This file is part of systemd. + + Copyright (C) 2014 Axis Communications AB. All rights reserved. + Copyright (C) 2015 Tom Gundersen + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see <http://www.gnu.org/licenses/>. +***/ + +#include <linux/filter.h> +#include <arpa/inet.h> + +#include "util.h" +#include "arp-util.h" + +int arp_network_bind_raw_socket(int ifindex, be32_t address, const struct ether_addr *eth_mac) { + struct sock_filter filter[] = { + BPF_STMT(BPF_LD + BPF_W + BPF_LEN, 0), /* A <- packet length */ + BPF_JUMP(BPF_JMP + BPF_JGE + BPF_K, sizeof(struct ether_arp), 1, 0), /* packet >= arp packet ? */ + BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */ + BPF_STMT(BPF_LD + BPF_H + BPF_ABS, offsetof(struct ether_arp, ea_hdr.ar_hrd)), /* A <- header */ + BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ARPHRD_ETHER, 1, 0), /* header == ethernet ? */ + BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */ + BPF_STMT(BPF_LD + BPF_H + BPF_ABS, offsetof(struct ether_arp, ea_hdr.ar_pro)), /* A <- protocol */ + BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ETHERTYPE_IP, 1, 0), /* protocol == IP ? */ + BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */ + BPF_STMT(BPF_LD + BPF_B + BPF_ABS, offsetof(struct ether_arp, ea_hdr.ar_hln)), /* A <- hardware address length */ + BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, sizeof(struct ether_addr), 1, 0), /* length == sizeof(ether_addr)? */ + BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */ + BPF_STMT(BPF_LD + BPF_B + BPF_ABS, offsetof(struct ether_arp, ea_hdr.ar_pln)), /* A <- protocol address length */ + BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, sizeof(struct in_addr), 1, 0), /* length == sizeof(in_addr) ? */ + BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */ + BPF_STMT(BPF_LD + BPF_H + BPF_ABS, offsetof(struct ether_arp, ea_hdr.ar_op)), /* A <- operation */ + BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ARPOP_REQUEST, 2, 0), /* protocol == request ? */ + BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ARPOP_REPLY, 1, 0), /* protocol == reply ? */ + BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */ + /* Sender Hardware Address must be different from our own */ + BPF_STMT(BPF_LD + BPF_IMM, htobe32(*((uint32_t *) eth_mac))), /* A <- 4 bytes of client's MAC */ + BPF_STMT(BPF_MISC + BPF_TAX, 0), /* X <- A */ + BPF_STMT(BPF_LD + BPF_W + BPF_ABS, offsetof(struct ether_arp, arp_sha)), /* A <- 4 bytes of SHA */ + BPF_STMT(BPF_ALU + BPF_XOR + BPF_X, 0), /* A xor X */ + BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0, 0, 6), /* A == 0 ? */ + BPF_STMT(BPF_LD + BPF_IMM, htobe16(*((uint16_t *) (((char *) eth_mac) + 4)))), /* A <- remainder of client's MAC */ + BPF_STMT(BPF_MISC + BPF_TAX, 0), /* X <- A */ + BPF_STMT(BPF_LD + BPF_H + BPF_ABS, offsetof(struct ether_arp, arp_sha) + 4), /* A <- remainder of SHA */ + BPF_STMT(BPF_ALU + BPF_XOR + BPF_X, 0), /* A xor X */ + BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0, 0, 1), /* A == 0 ? */ + BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */ + /* Sender Protocol Address or Target Protocol Address must be equal to the one we care about*/ + BPF_STMT(BPF_LD + BPF_IMM, htobe32(address)), /* A <- clients IP */ + BPF_STMT(BPF_MISC + BPF_TAX, 0), /* X <- A */ + BPF_STMT(BPF_LD + BPF_W + BPF_ABS, offsetof(struct ether_arp, arp_spa)), /* A <- SPA */ + BPF_STMT(BPF_ALU + BPF_XOR + BPF_X, 0), /* X xor A */ + BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0, 0, 1), /* A == 0 ? */ + BPF_STMT(BPF_RET + BPF_K, 65535), /* return all */ + BPF_STMT(BPF_LD + BPF_IMM, htobe32(address)), /* A <- clients IP */ + BPF_STMT(BPF_MISC + BPF_TAX, 0), /* X <- A */ + BPF_STMT(BPF_LD + BPF_W + BPF_ABS, offsetof(struct ether_arp, arp_tpa)), /* A <- TPA */ + BPF_STMT(BPF_ALU + BPF_XOR + BPF_X, 0), /* X xor A */ + BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0, 0, 1), /* A == 0 ? */ + BPF_STMT(BPF_RET + BPF_K, 65535), /* return all */ + BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */ + }; + struct sock_fprog fprog = { + .len = ELEMENTSOF(filter), + .filter = (struct sock_filter*) filter + }; + union sockaddr_union link = { + .ll.sll_family = AF_PACKET, + .ll.sll_protocol = htons(ETH_P_ARP), + .ll.sll_ifindex = ifindex, + .ll.sll_halen = ETH_ALEN, + .ll.sll_addr = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }, + }; + _cleanup_close_ int s = -1; + int r; + + assert(ifindex > 0); + + s = socket(PF_PACKET, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0); + if (s < 0) + return -errno; + + r = setsockopt(s, SOL_SOCKET, SO_ATTACH_FILTER, &fprog, sizeof(fprog)); + if (r < 0) + return -errno; + + r = bind(s, &link.sa, sizeof(link.ll)); + if (r < 0) + return -errno; + + r = s; + s = -1; + + return r; +} + +static int arp_send_packet(int fd, int ifindex, + be32_t pa, const struct ether_addr *ha, + bool announce) { + union sockaddr_union link = { + .ll.sll_family = AF_PACKET, + .ll.sll_protocol = htons(ETH_P_ARP), + .ll.sll_ifindex = ifindex, + .ll.sll_halen = ETH_ALEN, + .ll.sll_addr = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }, + }; + struct ether_arp arp = { + .ea_hdr.ar_hrd = htons(ARPHRD_ETHER), /* HTYPE */ + .ea_hdr.ar_pro = htons(ETHERTYPE_IP), /* PTYPE */ + .ea_hdr.ar_hln = ETH_ALEN, /* HLEN */ + .ea_hdr.ar_pln = sizeof(be32_t), /* PLEN */ + .ea_hdr.ar_op = htons(ARPOP_REQUEST), /* REQUEST */ + }; + int r; + + assert(fd >= 0); + assert(pa != 0); + assert(ha); + + memcpy(&arp.arp_sha, ha, ETH_ALEN); + memcpy(&arp.arp_tpa, &pa, sizeof(pa)); + + if (announce) + memcpy(&arp.arp_spa, &pa, sizeof(pa)); + + r = sendto(fd, &arp, sizeof(struct ether_arp), 0, &link.sa, sizeof(link.ll)); + if (r < 0) + return -errno; + + return 0; +} + +int arp_send_probe(int fd, int ifindex, + be32_t pa, const struct ether_addr *ha) { + return arp_send_packet(fd, ifindex, pa, ha, false); +} + +int arp_send_announcement(int fd, int ifindex, + be32_t pa, const struct ether_addr *ha) { + return arp_send_packet(fd, ifindex, pa, ha, true); +} diff --git a/src/libsystemd-network/ipv4ll-internal.h b/src/libsystemd-network/arp-util.h index ae0ce43985..44e5c893a7 100644 --- a/src/libsystemd-network/ipv4ll-internal.h +++ b/src/libsystemd-network/arp-util.h @@ -26,13 +26,9 @@ #include "sparse-endian.h" #include "socket-util.h" -int arp_network_bind_raw_socket(int index, union sockaddr_union *link); -int arp_network_send_raw_socket(int fd, const union sockaddr_union *link, - const struct ether_arp *arp); +int arp_network_bind_raw_socket(int index, be32_t address, const struct ether_addr *eth_mac); -void arp_packet_init(struct ether_arp *arp); -void arp_packet_probe(struct ether_arp *arp, be32_t pa, const struct ether_addr *ha); -void arp_packet_announcement(struct ether_arp *arp, be32_t pa, const struct ether_addr *ha); -int arp_packet_verify_headers(struct ether_arp *arp); - -#define log_ipv4ll(ll, fmt, ...) log_internal(LOG_DEBUG, 0, __FILE__, __LINE__, __func__, "IPv4LL: " fmt, ##__VA_ARGS__) +int arp_send_probe(int fd, int ifindex, + be32_t pa, const struct ether_addr *ha); +int arp_send_announcement(int fd, int ifindex, + be32_t pa, const struct ether_addr *ha); diff --git a/src/libsystemd-network/dhcp-server-internal.h b/src/libsystemd-network/dhcp-server-internal.h index 5dc3c7aa26..3b88b93d9a 100644 --- a/src/libsystemd-network/dhcp-server-internal.h +++ b/src/libsystemd-network/dhcp-server-internal.h @@ -96,5 +96,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/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 index 3c04898e92..4012cd483b 100644 --- a/src/libsystemd-network/lldp-internal.c +++ b/src/libsystemd-network/lldp-internal.c @@ -21,6 +21,7 @@ ***/ #include "lldp-internal.h" +#include "sd-lldp.h" /* We store maximum 1K chassis entries */ #define LLDP_MIB_MAX_CHASSIS 1024 @@ -28,207 +29,6 @@ /* 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 @@ -244,7 +44,7 @@ int lldp_mib_update_objects(lldp_chassis *c, tlv_packet *tlv) { assert_return(c, -EINVAL); assert_return(tlv, -EINVAL); - r = lldp_read_port_id(tlv, &type, &length, &data); + r = sd_lldp_packet_read_port_id(tlv, &type, &data, &length); if (r < 0) return r; @@ -253,13 +53,13 @@ int lldp_mib_update_objects(lldp_chassis *c, tlv_packet *tlv) { if ((p->type == type && p->length == length && !memcmp(p->data, data, p->length))) { - r = lldp_read_ttl(tlv, &ttl); + r = sd_lldp_packet_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); + sd_lldp_packet_unref(p->packet); p->packet = tlv; prioq_reshuffle(p->c->by_expiry, p, &p->prioq_idx); @@ -281,7 +81,7 @@ int lldp_mib_remove_objects(lldp_chassis *c, tlv_packet *tlv) { assert_return(c, -EINVAL); assert_return(tlv, -EINVAL); - r = lldp_read_port_id(tlv, &type, &length, &data); + r = sd_lldp_packet_read_port_id(tlv, &type, &data, &length); if (r < 0) return r; @@ -312,11 +112,11 @@ int lldp_mib_add_objects(Prioq *by_expiry, assert_return(neighbour_mib, -EINVAL); assert_return(tlv, -EINVAL); - r = lldp_read_chassis_id(tlv, &subtype, &length, &data); + r = sd_lldp_packet_read_chassis_id(tlv, &subtype, &data, &length); if (r < 0) goto drop; - r = lldp_read_ttl(tlv, &ttl); + r = sd_lldp_packet_read_ttl(tlv, &ttl); if (r < 0) goto drop; @@ -401,7 +201,7 @@ int lldp_mib_add_objects(Prioq *by_expiry, return 0; drop: - tlv_packet_free(tlv); + sd_lldp_packet_unref(tlv); if (new_chassis) hashmap_remove(neighbour_mib, &c->chassis_id); @@ -435,7 +235,7 @@ void lldp_neighbour_port_free(lldp_neighbour_port *p) { if(!p) return; - tlv_packet_free(p->packet); + sd_lldp_packet_unref(p->packet); free(p->data); free(p); @@ -452,11 +252,11 @@ int lldp_neighbour_port_new(lldp_chassis *c, assert(tlv); - r = lldp_read_port_id(tlv, &type, &length, &data); + r = sd_lldp_packet_read_port_id(tlv, &type, &data, &length); if (r < 0) return r; - r = lldp_read_ttl(tlv, &ttl); + r = sd_lldp_packet_read_ttl(tlv, &ttl); if (r < 0) return r; @@ -505,7 +305,7 @@ int lldp_chassis_new(tlv_packet *tlv, assert(tlv); - r = lldp_read_chassis_id(tlv, &type, &length, &data); + r = sd_lldp_packet_read_chassis_id(tlv, &type, &data, &length); if (r < 0) return r; @@ -531,3 +331,30 @@ int lldp_chassis_new(tlv_packet *tlv, return 0; } + +int lldp_receive_packet(sd_event_source *s, int fd, uint32_t revents, void *userdata) { + _cleanup_lldp_packet_unref_ 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-internal.h b/src/libsystemd-network/lldp-internal.h index f4eadbb87e..284cc6720e 100644 --- a/src/libsystemd-network/lldp-internal.h +++ b/src/libsystemd-network/lldp-internal.h @@ -26,6 +26,7 @@ #include "list.h" #include "lldp-tlv.h" #include "prioq.h" +#include "sd-event.h" typedef struct lldp_neighbour_port lldp_neighbour_port; typedef struct lldp_chassis lldp_chassis; @@ -86,13 +87,6 @@ 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); +int lldp_receive_packet(sd_event_source *s, int fd, uint32_t revents, void *userdata); #define log_lldp(fmt, ...) log_internal(LOG_DEBUG, 0, __FILE__, __LINE__, __func__, "LLDP: " fmt, ##__VA_ARGS__) diff --git a/src/libsystemd-network/lldp-network.c b/src/libsystemd-network/lldp-network.c index 664d2f7867..12a6599ff1 100644 --- a/src/libsystemd-network/lldp-network.c +++ b/src/libsystemd-network/lldp-network.c @@ -82,30 +82,3 @@ int lldp_network_bind_raw_socket(int ifindex) { 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..74ee13a414 100644 --- a/src/libsystemd-network/lldp-network.h +++ b/src/libsystemd-network/lldp-network.h @@ -25,4 +25,3 @@ #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 index aa6a3b9224..7486b4c38f 100644 --- a/src/libsystemd-network/lldp-port.c +++ b/src/libsystemd-network/lldp-port.c @@ -23,6 +23,7 @@ #include "async.h" #include "lldp-port.h" #include "lldp-network.h" +#include "lldp-internal.h" int lldp_port_start(lldp_port *p) { int r; @@ -38,19 +39,19 @@ int lldp_port_start(lldp_port *p) { 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; + log_debug_errno(r, "Failed to allocate event source: %m"); + goto fail; } 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)); + log_debug_errno(r, "Failed to set event priority: %m"); 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)); + log_debug_errno(r, "Failed to set event name: %m"); goto fail; } diff --git a/src/libsystemd-network/lldp-port.h b/src/libsystemd-network/lldp-port.h index b2d3180091..517b162a67 100644 --- a/src/libsystemd-network/lldp-port.h +++ b/src/libsystemd-network/lldp-port.h @@ -31,6 +31,14 @@ typedef struct lldp_port lldp_port; +typedef enum LLDPPortStatus { + LLDP_PORT_STATUS_NONE, + LLDP_PORT_STATUS_ENABLED, + LLDP_PORT_STATUS_DISABLED, + _LLDP_PORT_STATUS_MAX, + _LLDP_PORT_STATUS_INVALID = -1, +} LLDPPortStatus; + struct lldp_port { LLDPPortStatus status; diff --git a/src/libsystemd-network/lldp-tlv.c b/src/libsystemd-network/lldp-tlv.c index 0cea5b10a6..66af22e37d 100644 --- a/src/libsystemd-network/lldp-tlv.c +++ b/src/libsystemd-network/lldp-tlv.c @@ -54,22 +54,41 @@ int tlv_packet_new(tlv_packet **ret) { return -ENOMEM; LIST_HEAD_INIT(m->sections); + m->n_ref = 1; *ret = m; return 0; } -void tlv_packet_free(tlv_packet *m) { +tlv_packet *sd_lldp_packet_ref(tlv_packet *m) { + + if (!m) + return NULL; + + assert(m->n_ref > 0); + m->n_ref++; + + return m; +} + +tlv_packet *sd_lldp_packet_unref(tlv_packet *m) { tlv_section *s, *n; if (!m) - return; + return NULL; + + assert(m->n_ref > 0); + m->n_ref--; + + if (m->n_ref > 0) + return m; LIST_FOREACH_SAFE(section, s, n, m->sections) tlv_section_free(s); free(m); + return NULL; } int tlv_packet_append_bytes(tlv_packet *m, const void *data, size_t data_length) { @@ -221,9 +240,9 @@ int tlv_packet_read_string(tlv_packet *m, char **data, uint16_t *data_length) { return r; *data = (char *) val; - *data_length = m->container->length; + *data_length = m->container->data + m->container->length - m->container->read_pos; - m->container->read_pos += m->container->length; + m->container->read_pos += *data_length; return 0; } @@ -239,9 +258,9 @@ int tlv_packet_read_bytes(tlv_packet *m, uint8_t **data, uint16_t *data_length) return r; *data = (uint8_t *) val; - *data_length = m->container->length; + *data_length = m->container->data + m->container->length - m->container->read_pos; - m->container->read_pos += m->container->length; + m->container->read_pos += *data_length; return 0; } @@ -258,7 +277,7 @@ int tlv_packet_parse_pdu(tlv_packet *m, uint16_t size) { p = m->pdu; - /* extract ethernet herader */ + /* extract ethernet header */ memcpy(&m->mac, p, ETH_ALEN); p += sizeof(struct ether_header); @@ -278,6 +297,17 @@ int tlv_packet_parse_pdu(tlv_packet *m, uint16_t size) { } p += 2; + + if (section->type == LLDP_TYPE_PRIVATE && + section->length >= LLDP_OUI_LEN + 1) { + section->oui = p; + p += LLDP_OUI_LEN; + section->subtype = *p++; + + section->length -= LLDP_OUI_LEN + 1; + l += LLDP_OUI_LEN + 1; + } + section->data = p; LIST_FIND_TAIL(section, m->sections, tail); @@ -294,6 +324,7 @@ int lldp_tlv_packet_enter_container(tlv_packet *m, uint16_t type) { tlv_section *s; assert_return(m, -EINVAL); + assert_return(type != LLDP_TYPE_PRIVATE, -EINVAL); LIST_FOREACH(section, s, m->sections) if (s->type == type) @@ -305,7 +336,35 @@ int lldp_tlv_packet_enter_container(tlv_packet *m, uint16_t type) { m->container->read_pos = s->data; if (!m->container->read_pos) { - m->container = 0; + m->container = NULL; + return -1; + } + + return 0; +} + +int lldp_tlv_packet_enter_container_oui(tlv_packet *m, const uint8_t *oui, uint8_t subtype) { + tlv_section *s; + + assert_return(m, -EINVAL); + assert_return(oui, -EINVAL); + + LIST_FOREACH(section, s, m->sections) { + if (s->type == LLDP_TYPE_PRIVATE && + s->oui && + s->subtype == subtype && + !memcmp(s->oui, oui, LLDP_OUI_LEN)) + break; + } + + if (!s) + return -1; + + m->container = s; + + m->container->read_pos = s->data; + if (!m->container->read_pos) { + m->container = NULL; return -1; } @@ -319,3 +378,270 @@ int lldp_tlv_packet_exit_container(tlv_packet *m) { return 0; } + +static int lldp_tlv_packet_read_u16_tlv(tlv_packet *tlv, uint16_t type, uint16_t *value) { + int r, r2; + + assert_return(tlv, -EINVAL); + + r = lldp_tlv_packet_enter_container(tlv, type); + if (r < 0) + goto out; + + r = tlv_packet_read_u16(tlv, value); + r2 = lldp_tlv_packet_exit_container(tlv); + + out: + return r < 0 ? r : r2; +} + +static int lldp_tlv_packet_read_string_tlv(tlv_packet *tlv, uint16_t type, char **data, uint16_t *length) { + char *s; + int r, r2; + + assert_return(tlv, -EINVAL); + + r = lldp_tlv_packet_enter_container(tlv, type); + if (r < 0) + return r; + + r = tlv_packet_read_string(tlv, &s, length); + if (r < 0) + goto out; + + *data = (char *) s; + + out: + r2 = lldp_tlv_packet_exit_container(tlv); + + return r < 0 ? r : r2; +} + +int sd_lldp_packet_read_chassis_id(tlv_packet *tlv, + uint8_t *type, + uint8_t **data, + uint16_t *length) { + uint8_t subtype; + int r, r2; + + 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: + r2 = lldp_tlv_packet_exit_container(tlv); + + out2: + return r < 0 ? r : r2; +} + +int sd_lldp_packet_read_port_id(tlv_packet *tlv, + uint8_t *type, + uint8_t **data, + uint16_t *length) { + uint8_t subtype; + char *s; + int r, r2; + + 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: + r2 = lldp_tlv_packet_exit_container(tlv); + + out2: + return r < 0 ? r : r2; +} + +int sd_lldp_packet_read_ttl(tlv_packet *tlv, uint16_t *ttl) { + return lldp_tlv_packet_read_u16_tlv(tlv, LLDP_TYPE_TTL, ttl); +} + +int sd_lldp_packet_read_system_name(tlv_packet *tlv, + char **data, + uint16_t *length) { + return lldp_tlv_packet_read_string_tlv(tlv, LLDP_TYPE_SYSTEM_NAME, data, length); +} + +int sd_lldp_packet_read_system_description(tlv_packet *tlv, + char **data, + uint16_t *length) { + return lldp_tlv_packet_read_string_tlv(tlv, LLDP_TYPE_SYSTEM_DESCRIPTION, data, length); +} + +int sd_lldp_packet_read_port_description(tlv_packet *tlv, + char **data, + uint16_t *length) { + return lldp_tlv_packet_read_string_tlv(tlv, LLDP_TYPE_PORT_DESCRIPTION, data, length); +} + +int sd_lldp_packet_read_system_capability(tlv_packet *tlv, uint16_t *data) { + return lldp_tlv_packet_read_u16_tlv(tlv, LLDP_TYPE_SYSTEM_CAPABILITIES, data); +} + +int sd_lldp_packet_read_port_vlan_id(tlv_packet *tlv, uint16_t *id) { + int r, r2; + + assert_return(tlv, -EINVAL); + + r = lldp_tlv_packet_enter_container_oui(tlv, LLDP_OUI_802_1, LLDP_OUI_SUBTYPE_802_1_PORT_VLAN_ID); + if (r < 0) + goto out; + + r = tlv_packet_read_u16(tlv, id); + r2 = lldp_tlv_packet_exit_container(tlv); + + out: + return r < 0 ? r : r2; +} + +int sd_lldp_packet_read_port_protocol_vlan_id(sd_lldp_packet *tlv, uint8_t *flags, uint16_t *id) { + int r, r2; + + assert_return(tlv, -EINVAL); + + r = lldp_tlv_packet_enter_container_oui(tlv, LLDP_OUI_802_1, LLDP_OUI_SUBTYPE_802_1_PORT_PROTOCOL_VLAN_ID); + if (r < 0) + goto out; + + r = tlv_packet_read_u8(tlv, flags); + if (r >= 0) + r = tlv_packet_read_u16(tlv, id); + + r2 = lldp_tlv_packet_exit_container(tlv); + + out: + return r < 0 ? r : r2; +} + +int sd_lldp_packet_read_vlan_name(tlv_packet *tlv, uint16_t *vlan_id, char **name, uint16_t *length) { + int r, r2; + uint8_t len = 0; + + assert_return(tlv, -EINVAL); + + r = lldp_tlv_packet_enter_container_oui(tlv, LLDP_OUI_802_1, LLDP_OUI_SUBTYPE_802_1_VLAN_NAME); + if (r < 0) + goto out; + + r = tlv_packet_read_u16(tlv, vlan_id); + if (r >= 0) + r = tlv_packet_read_u8(tlv, &len); + if (r >= 0) + r = tlv_packet_read_string(tlv, name, length); + + if (r >= 0 && len < *length) + *length = len; + + r2 = lldp_tlv_packet_exit_container(tlv); + + out: + return r < 0 ? r : r2; +} + +int sd_lldp_packet_read_management_vid(tlv_packet *tlv, uint16_t *id) { + int r, r2; + + assert_return(tlv, -EINVAL); + + r = lldp_tlv_packet_enter_container_oui(tlv, LLDP_OUI_802_1, LLDP_OUI_SUBTYPE_802_1_MANAGEMENT_VID); + if (r < 0) + goto out; + + r = tlv_packet_read_u16(tlv, id); + r2 = lldp_tlv_packet_exit_container(tlv); + + out: + return r < 0 ? r : r2; +} + +int sd_lldp_packet_read_link_aggregation(sd_lldp_packet *tlv, uint8_t *status, uint32_t *id) { + int r, r2; + + assert_return(tlv, -EINVAL); + + r = lldp_tlv_packet_enter_container_oui(tlv, LLDP_OUI_802_1, LLDP_OUI_SUBTYPE_802_1_LINK_AGGREGATION); + if (r < 0) + goto out; + + r = tlv_packet_read_u8(tlv, status); + if (r >= 0) + r = tlv_packet_read_u32(tlv, id); + + r2 = lldp_tlv_packet_exit_container(tlv); + + out: + return r < 0 ? r : r2; +} + +int sd_lldp_packet_get_destination_type(tlv_packet *tlv, int *dest) { + assert_return(tlv, -EINVAL); + assert_return(dest, -EINVAL); + + /* 802.1AB-2009, Table 7-1 */ + if (!memcmp(&tlv->mac, LLDP_MAC_NEAREST_BRIDGE, ETH_ALEN)) + *dest = SD_LLDP_DESTINATION_TYPE_NEAREST_BRIDGE; + else if (!memcmp(&tlv->mac, LLDP_MAC_NEAREST_NON_TPMR_BRIDGE, ETH_ALEN)) + *dest = SD_LLDP_DESTINATION_TYPE_NEAREST_NON_TPMR_BRIDGE; + else if (!memcmp(&tlv->mac, LLDP_MAC_NEAREST_CUSTOMER_BRIDGE, ETH_ALEN)) + *dest = SD_LLDP_DESTINATION_TYPE_NEAREST_CUSTOMER_BRIDGE; + else + return -EINVAL; + + return 0; +} diff --git a/src/libsystemd-network/lldp-tlv.h b/src/libsystemd-network/lldp-tlv.h index ce3334e115..ca1da113d5 100644 --- a/src/libsystemd-network/lldp-tlv.h +++ b/src/libsystemd-network/lldp-tlv.h @@ -28,12 +28,18 @@ #include "lldp.h" #include "list.h" -typedef struct tlv_packet tlv_packet; -typedef struct tlv_section tlv_section; +#include "sd-lldp.h" -struct tlv_section { +typedef struct sd_lldp_packet tlv_packet; +typedef struct sd_lldp_section tlv_section; + +#define LLDP_OUI_LEN 3 + +struct sd_lldp_section { uint16_t type; uint16_t length; + uint8_t *oui; + uint8_t subtype; uint8_t *read_pos; uint8_t *data; @@ -41,10 +47,16 @@ struct tlv_section { LIST_FIELDS(tlv_section, section); }; +#define LLDP_MAC_NEAREST_BRIDGE (uint8_t[]) { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x0e } +#define LLDP_MAC_NEAREST_NON_TPMR_BRIDGE (uint8_t[]) { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x03 } +#define LLDP_MAC_NEAREST_CUSTOMER_BRIDGE (uint8_t[]) { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x00 } + int tlv_section_new(tlv_section **ret); void tlv_section_free(tlv_section *ret); -struct tlv_packet { +struct sd_lldp_packet { + unsigned n_ref; + uint16_t type; uint16_t length; usec_t ts; @@ -61,10 +73,9 @@ struct tlv_packet { }; 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) +DEFINE_TRIVIAL_CLEANUP_FUNC(sd_lldp_packet*, sd_lldp_packet_unref); +#define _cleanup_lldp_packet_unref_ _cleanup_(sd_lldp_packet_unrefp) int lldp_tlv_packet_open_container(tlv_packet *m, uint16_t type); int lldp_tlv_packet_close_container(tlv_packet *m); @@ -76,6 +87,7 @@ 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_enter_container_oui(tlv_packet *m, const uint8_t *oui, uint8_t subtype); int lldp_tlv_packet_exit_container(tlv_packet *m); int tlv_packet_read_bytes(tlv_packet *m, uint8_t **data, uint16_t *data_length); diff --git a/src/libsystemd-network/lldp.h b/src/libsystemd-network/lldp.h index 5e4b283e26..19e5cc5f41 100644 --- a/src/libsystemd-network/lldp.h +++ b/src/libsystemd-network/lldp.h @@ -113,3 +113,16 @@ typedef enum LLDPMedCapability { LLDP_MED_CAPABILITY_MAX, LLDP_MED_CAPABILITY_INVALID = -1, } LLDPMedCapability; + +#define LLDP_OUI_802_1 (uint8_t[]) { 0x00, 0x80, 0xc2 } +#define LLDP_OUI_802_3 (uint8_t[]) { 0x00, 0x12, 0x0f } + +enum { + LLDP_OUI_SUBTYPE_802_1_PORT_VLAN_ID = 1, + LLDP_OUI_SUBTYPE_802_1_PORT_PROTOCOL_VLAN_ID = 2, + LLDP_OUI_SUBTYPE_802_1_VLAN_NAME = 3, + LLDP_OUI_SUBTYPE_802_1_PROTOCOL_IDENTITY = 4, + LLDP_OUI_SUBTYPE_802_1_VID_USAGE_DIGEST = 5, + LLDP_OUI_SUBTYPE_802_1_MANAGEMENT_VID = 6, + LLDP_OUI_SUBTYPE_802_1_LINK_AGGREGATION = 7, +}; diff --git a/src/libsystemd-network/network-internal.c b/src/libsystemd-network/network-internal.c index 26bd4088d9..2a62af2fd4 100644 --- a/src/libsystemd-network/network-internal.c +++ b/src/libsystemd-network/network-internal.c @@ -196,8 +196,7 @@ int config_parse_ifname(const char *unit, 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); + log_syntax(unit, LOG_ERR, filename, line, 0, "Interface name is not ASCII clean or is too long, ignoring assignment: %s", rvalue); return 0; } @@ -240,8 +239,7 @@ int config_parse_ifnames(const char *unit, 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); + log_syntax(unit, LOG_ERR, filename, line, 0, "Interface name is not ASCII clean or is too long, ignoring assignment: %s", rvalue); free(n); return 0; } @@ -278,8 +276,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; } @@ -324,8 +321,7 @@ int config_parse_hwaddr(const char *unit, &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); + log_syntax(unit, LOG_ERR, filename, line, 0, "Not a valid MAC address, ignoring assignment: %s", rvalue); free(n); return 0; } @@ -394,8 +390,8 @@ void serialize_in6_addrs(FILE *f, const struct in6_addr *addresses, assert(size); for (i = 0; i < size; i++) - fprintf(f, SD_ICMP6_ADDRESS_FORMAT_STR"%s", - SD_ICMP6_ADDRESS_FORMAT_VAL(addresses[i]), + fprintf(f, SD_ICMP6_ND_ADDRESS_FORMAT_STR"%s", + SD_ICMP6_ND_ADDRESS_FORMAT_VAL(addresses[i]), (i < (size - 1)) ? " ": ""); } diff --git a/src/libsystemd-network/sd-dhcp-client.c b/src/libsystemd-network/sd-dhcp-client.c index c12768cf0e..28e012afca 100644 --- a/src/libsystemd-network/sd-dhcp-client.c +++ b/src/libsystemd-network/sd-dhcp-client.c @@ -213,7 +213,7 @@ int sd_dhcp_client_set_mac(sd_dhcp_client *client, const uint8_t *addr, 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); @@ -277,7 +277,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; @@ -385,7 +385,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"); @@ -983,7 +983,7 @@ static int client_timeout_expire(sd_event_source *s, uint64_t usec, 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) { @@ -1143,14 +1143,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); } @@ -1265,8 +1265,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) @@ -1292,8 +1291,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) @@ -1318,8 +1316,7 @@ 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; } @@ -1382,8 +1379,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; @@ -1633,7 +1630,7 @@ 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; diff --git a/src/libsystemd-network/sd-dhcp-lease.c b/src/libsystemd-network/sd-dhcp-lease.c index aa07846693..df3d8e6e3c 100644 --- a/src/libsystemd-network/sd-dhcp-lease.c +++ b/src/libsystemd-network/sd-dhcp-lease.c @@ -314,10 +314,14 @@ static int lease_parse_string(const uint8_t *option, size_t len, char **ret) { else { char *string; - if (memchr(option, 0, 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); + string = strndup((const char *) option, len); if (!string) return -ENOMEM; diff --git a/src/libsystemd-network/sd-dhcp-server.c b/src/libsystemd-network/sd-dhcp-server.c index 1f167485e3..d27bb561ca 100644 --- a/src/libsystemd-network/sd-dhcp-server.c +++ b/src/libsystemd-network/sd-dhcp-server.c @@ -110,18 +110,15 @@ sd_dhcp_server *sd_dhcp_server_ref(sd_dhcp_server *server) { 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) { @@ -743,13 +740,18 @@ 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 */ - next_offer = client_id_hash_func(&req->client_id, HASH_KEY.bytes) % server->pool_size; + siphash24_init(&state, HASH_KEY.bytes); + client_id_hash_func(&req->client_id, &state); + siphash24_finalize((uint8_t*)&hash, &state); + next_offer = hash % server->pool_size; for (i = 0; i < server->pool_size; i++) { if (!server->bound_leases[next_offer]) { diff --git a/src/libsystemd-network/sd-dhcp6-client.c b/src/libsystemd-network/sd-dhcp6-client.c index 5489c77864..9cd4bd3032 100644 --- a/src/libsystemd-network/sd-dhcp6-client.c +++ b/src/libsystemd-network/sd-dhcp6-client.c @@ -125,6 +125,8 @@ int sd_dhcp6_client_set_index(sd_dhcp6_client *client, int interface_index) { assert_return(client, -EINVAL); assert_return(interface_index >= -1, -EINVAL); + assert_return(IN_SET(client->state, DHCP6_STATE_STOPPED), -EBUSY); + client->index = interface_index; return 0; @@ -140,6 +142,8 @@ int sd_dhcp6_client_set_mac( 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) @@ -173,6 +177,8 @@ int sd_dhcp6_client_set_duid( assert_return(duid, -EINVAL); assert_return(duid_len > 0 && duid_len <= MAX_DUID_LEN, -EINVAL); + assert_return(IN_SET(client->state, DHCP6_STATE_STOPPED), -EBUSY); + switch (type) { case DHCP6_DUID_LLT: if (duid_len <= sizeof(client->duid.llt)) @@ -205,6 +211,8 @@ int sd_dhcp6_client_set_duid( int sd_dhcp6_client_set_information_request(sd_dhcp6_client *client, bool enabled) { assert_return(client, -EINVAL); + assert_return(IN_SET(client->state, DHCP6_STATE_STOPPED), -EBUSY); + client->information_request = enabled; return 0; @@ -266,13 +274,18 @@ static void client_notify(sd_dhcp6_client *client, int event) { client->cb(client, event, client->userdata); } -static int client_reset(sd_dhcp6_client *client) { - assert_return(client, -EINVAL); - +static void client_set_lease(sd_dhcp6_client *client, sd_dhcp6_lease *lease) { if (client->lease) { dhcp6_lease_clear_timers(&client->lease->ia); - client->lease = sd_dhcp6_lease_unref(client->lease); + sd_dhcp6_lease_unref(client->lease); } + client->lease = lease; +} + +static int client_reset(sd_dhcp6_client *client) { + assert_return(client, -EINVAL); + + client_set_lease(client, NULL); client->receive_message = sd_event_source_unref(client->receive_message); @@ -464,7 +477,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..." */ @@ -554,7 +567,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; } @@ -582,8 +595,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(), @@ -826,12 +838,7 @@ static int client_receive_reply(sd_dhcp6_client *client, DHCP6Message *reply, si return 0; } - if (client->lease) { - dhcp6_lease_clear_timers(&client->lease->ia); - client->lease = sd_dhcp6_lease_unref(client->lease); - } - - client->lease = lease; + client_set_lease(client, lease); lease = NULL; return DHCP6_STATE_BOUND; @@ -860,8 +867,7 @@ static int client_receive_advertise(sd_dhcp6_client *client, DHCP6Message *adver 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; } @@ -930,7 +936,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); @@ -962,7 +968,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; @@ -1041,9 +1047,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, @@ -1065,9 +1069,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, @@ -1113,7 +1115,7 @@ static int client_start(sd_dhcp6_client *client, enum DHCP6State state) { } int sd_dhcp6_client_stop(sd_dhcp6_client *client) { - client_stop(client, DHCP6_EVENT_STOP); + client_stop(client, SD_DHCP6_CLIENT_EVENT_STOP); return 0; } @@ -1126,6 +1128,9 @@ int sd_dhcp6_client_start(sd_dhcp6_client *client) { assert_return(client->event, -EINVAL); assert_return(client->index > 0, -EINVAL); + if (!IN_SET(client->state, DHCP6_STATE_STOPPED)) + return -EALREADY; + r = client_reset(client); if (r < 0) return r; @@ -1233,7 +1238,6 @@ sd_dhcp6_client *sd_dhcp6_client_unref(sd_dhcp6_client *client) { client_reset(client); sd_dhcp6_client_detach_event(client); - sd_dhcp6_lease_unref(client->lease); free(client->req_opts); free(client); diff --git a/src/libsystemd-network/sd-icmp6-nd.c b/src/libsystemd-network/sd-icmp6-nd.c index e80232a7e0..f014cac628 100644 --- a/src/libsystemd-network/sd-icmp6-nd.c +++ b/src/libsystemd-network/sd-icmp6-nd.c @@ -274,15 +274,15 @@ static int icmp6_ra_prefix_timeout(sd_event_source *s, uint64_t usec, 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), + log_icmp6_nd(nd, "Prefix expired "SD_ICMP6_ND_ADDRESS_FORMAT_STR"/%d", + SD_ICMP6_ND_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); + SD_ICMP6_ND_EVENT_ROUTER_ADVERTISMENT_PREFIX_EXPIRED); nd->expired_prefix = NULL; prefix = icmp6_prefix_unref(prefix); @@ -441,11 +441,10 @@ static int icmp6_ra_prefix_update(sd_icmp6_nd *nd, ssize_t 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), + log_icmp6_nd(nd, "New prefix "SD_ICMP6_ND_ADDRESS_FORMAT_STR"/%d lifetime %d expires in %s", + SD_ICMP6_ND_ADDRESS_FORMAT_VAL(prefix->addr), prefix->len, lifetime, - format_timespan(time_string, FORMAT_TIMESPAN_MAX, - lifetime * USEC_PER_SEC, 0)); + format_timespan(time_string, FORMAT_TIMESPAN_MAX, lifetime * USEC_PER_SEC, USEC_PER_SEC)); LIST_PREPEND(prefixes, nd->prefixes, prefix); @@ -463,11 +462,10 @@ static int icmp6_ra_prefix_update(sd_icmp6_nd *nd, ssize_t len, 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), + log_icmp6_nd(nd, "Update prefix "SD_ICMP6_ND_ADDRESS_FORMAT_STR"/%d lifetime %d expires in %s", + SD_ICMP6_ND_ADDRESS_FORMAT_VAL(prefix->addr), prefix->len, lifetime, - format_timespan(time_string, FORMAT_TIMESPAN_MAX, - lifetime * USEC_PER_SEC, 0)); + format_timespan(time_string, FORMAT_TIMESPAN_MAX, lifetime * USEC_PER_SEC, USEC_PER_SEC)); } r = icmp6_ra_prefix_set_timeout(nd, prefix, lifetime * USEC_PER_SEC); @@ -541,7 +539,7 @@ static int icmp6_router_advertisment_recv(sd_event_source *s, int fd, uint32_t r int r, buflen = 0; ssize_t len; _cleanup_free_ struct nd_router_advert *ra = NULL; - int event = ICMP6_EVENT_ROUTER_ADVERTISMENT_NONE; + int event = SD_ICMP6_ND_EVENT_ROUTER_ADVERTISMENT_NONE; assert(s); assert(nd); @@ -572,16 +570,16 @@ static int icmp6_router_advertisment_recv(sd_event_source *s, int fd, uint32_t r nd->state = ICMP6_ROUTER_ADVERTISMENT_LISTEN; if (ra->nd_ra_flags_reserved & ND_RA_FLAG_OTHER ) - event = ICMP6_EVENT_ROUTER_ADVERTISMENT_OTHER; + event = SD_ICMP6_ND_EVENT_ROUTER_ADVERTISMENT_OTHER; if (ra->nd_ra_flags_reserved & ND_RA_FLAG_MANAGED) - event = ICMP6_EVENT_ROUTER_ADVERTISMENT_MANAGED; + event = SD_ICMP6_ND_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) { + if (event != SD_ICMP6_ND_EVENT_ROUTER_ADVERTISMENT_NONE) { r = icmp6_ra_parse(nd, ra, len); if (r < 0) { log_icmp6_nd(nd, "Could not parse Router Advertisement: %s", @@ -609,7 +607,7 @@ static int icmp6_router_solicitation_timeout(sd_event_source *s, uint64_t usec, 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); + icmp6_nd_notify(nd, SD_ICMP6_ND_EVENT_ROUTER_ADVERTISMENT_TIMEOUT); nd->state = ICMP6_ROUTER_ADVERTISMENT_LISTEN; } else { if (memcmp(&nd->mac_addr, &unset, sizeof(struct ether_addr))) diff --git a/src/libsystemd-network/sd-ipv4acd.c b/src/libsystemd-network/sd-ipv4acd.c new file mode 100644 index 0000000000..95b96bfd52 --- /dev/null +++ b/src/libsystemd-network/sd-ipv4acd.c @@ -0,0 +1,529 @@ +/*** + This file is part of systemd. + + Copyright (C) 2014 Axis Communications AB. All rights reserved. + Copyright (C) 2015 Tom Gundersen + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see <http://www.gnu.org/licenses/>. +***/ + +#include <arpa/inet.h> +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "event-util.h" +#include "in-addr-util.h" +#include "list.h" +#include "refcnt.h" +#include "random-util.h" +#include "siphash24.h" +#include "util.h" + +#include "arp-util.h" +#include "sd-ipv4acd.h" + +/* Constants from the RFC */ +#define PROBE_WAIT 1 +#define PROBE_NUM 3 +#define PROBE_MIN 1 +#define PROBE_MAX 2 +#define ANNOUNCE_WAIT 2 +#define ANNOUNCE_NUM 2 +#define ANNOUNCE_INTERVAL 2 +#define MAX_CONFLICTS 10 +#define RATE_LIMIT_INTERVAL 60 +#define DEFEND_INTERVAL 10 + +#define IPV4ACD_NETWORK 0xA9FE0000L +#define IPV4ACD_NETMASK 0xFFFF0000L + +#define log_ipv4acd_full(ll, level, error, fmt, ...) log_internal(level, error, __FILE__, __LINE__, __func__, "ACD: " fmt, ##__VA_ARGS__) + +#define log_ipv4acd_debug(ll, ...) log_ipv4acd_full(ll, LOG_DEBUG, 0, ##__VA_ARGS__) +#define log_ipv4acd_info(ll, ...) log_ipv4acd_full(ll, LOG_INFO, 0, ##__VA_ARGS__) +#define log_ipv4acd_notice(ll, ...) log_ipv4acd_full(ll, LOG_NOTICE, 0, ##__VA_ARGS__) +#define log_ipv4acd_warning(ll, ...) log_ipv4acd_full(ll, LOG_WARNING, 0, ##__VA_ARGS__) +#define log_ipv4acd_error(ll, ...) log_ipv4acd_full(ll, LOG_ERR, 0, ##__VA_ARGS__) + +#define log_ipv4acd_debug_errno(ll, error, ...) log_ipv4acd_full(ll, LOG_DEBUG, error, ##__VA_ARGS__) +#define log_ipv4acd_info_errno(ll, error, ...) log_ipv4acd_full(ll, LOG_INFO, error, ##__VA_ARGS__) +#define log_ipv4acd_notice_errno(ll, error, ...) log_ipv4acd_full(ll, LOG_NOTICE, error, ##__VA_ARGS__) +#define log_ipv4acd_warning_errno(ll, error, ...) log_ipv4acd_full(ll, LOG_WARNING, error, ##__VA_ARGS__) +#define log_ipv4acd_error_errno(ll, error, ...) log_ipv4acd_full(ll, LOG_ERR, error, ##__VA_ARGS__) + +typedef enum IPv4ACDState { + IPV4ACD_STATE_INIT, + IPV4ACD_STATE_WAITING_PROBE, + IPV4ACD_STATE_PROBING, + IPV4ACD_STATE_WAITING_ANNOUNCE, + IPV4ACD_STATE_ANNOUNCING, + IPV4ACD_STATE_RUNNING, + _IPV4ACD_STATE_MAX, + _IPV4ACD_STATE_INVALID = -1 +} IPv4ACDState; + +struct sd_ipv4acd { + RefCount n_ref; + + IPv4ACDState state; + int index; + int fd; + int iteration; + int conflict; + sd_event_source *receive_message; + sd_event_source *timer; + usec_t defend_window; + be32_t address; + /* External */ + struct ether_addr mac_addr; + sd_event *event; + int event_priority; + sd_ipv4acd_cb_t cb; + void* userdata; +}; + +sd_ipv4acd *sd_ipv4acd_ref(sd_ipv4acd *ll) { + if (ll) + assert_se(REFCNT_INC(ll->n_ref) >= 2); + + return ll; +} + +sd_ipv4acd *sd_ipv4acd_unref(sd_ipv4acd *ll) { + if (!ll || REFCNT_DEC(ll->n_ref) > 0) + return NULL; + + ll->receive_message = sd_event_source_unref(ll->receive_message); + ll->fd = safe_close(ll->fd); + + ll->timer = sd_event_source_unref(ll->timer); + + sd_ipv4acd_detach_event(ll); + + free(ll); + + return NULL; +} + +DEFINE_TRIVIAL_CLEANUP_FUNC(sd_ipv4acd*, sd_ipv4acd_unref); +#define _cleanup_ipv4acd_unref_ _cleanup_(sd_ipv4acd_unrefp) + +int sd_ipv4acd_new(sd_ipv4acd **ret) { + _cleanup_ipv4acd_unref_ sd_ipv4acd *ll = NULL; + + assert_return(ret, -EINVAL); + + ll = new0(sd_ipv4acd, 1); + if (!ll) + return -ENOMEM; + + ll->n_ref = REFCNT_INIT; + ll->state = IPV4ACD_STATE_INIT; + ll->index = -1; + ll->fd = -1; + + *ret = ll; + ll = NULL; + + return 0; +} + +static void ipv4acd_set_state(sd_ipv4acd *ll, IPv4ACDState st, bool reset_counter) { + + assert(ll); + assert(st < _IPV4ACD_STATE_MAX); + + if (st == ll->state && !reset_counter) + ll->iteration++; + else { + ll->state = st; + ll->iteration = 0; + } +} + +static void ipv4acd_client_notify(sd_ipv4acd *ll, int event) { + assert(ll); + + if (ll->cb) + ll->cb(ll, event, ll->userdata); +} + +static void ipv4acd_stop(sd_ipv4acd *ll) { + assert(ll); + + ll->receive_message = sd_event_source_unref(ll->receive_message); + ll->fd = safe_close(ll->fd); + + ll->timer = sd_event_source_unref(ll->timer); + + log_ipv4acd_debug(ll, "STOPPED"); + + ipv4acd_set_state (ll, IPV4ACD_STATE_INIT, true); +} + +int sd_ipv4acd_stop(sd_ipv4acd *ll) { + assert_return(ll, -EINVAL); + + ipv4acd_stop(ll); + + ipv4acd_client_notify(ll, 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 *ll, int sec, int random_sec) { + _cleanup_event_source_unref_ sd_event_source *timer = NULL; + usec_t next_timeout; + usec_t time_now; + int r; + + assert(sec >= 0); + assert(random_sec >= 0); + assert(ll); + + next_timeout = sec * USEC_PER_SEC; + + if (random_sec) + next_timeout += random_u32() % (random_sec * USEC_PER_SEC); + + assert_se(sd_event_now(ll->event, clock_boottime_or_monotonic(), &time_now) >= 0); + + r = sd_event_add_time(ll->event, &timer, clock_boottime_or_monotonic(), + time_now + next_timeout, 0, ipv4acd_on_timeout, ll); + if (r < 0) + return r; + + r = sd_event_source_set_priority(timer, ll->event_priority); + if (r < 0) + return r; + + r = sd_event_source_set_description(timer, "ipv4acd-timer"); + if (r < 0) + return r; + + ll->timer = sd_event_source_unref(ll->timer); + ll->timer = timer; + timer = NULL; + + return 0; +} + +static bool ipv4acd_arp_conflict(sd_ipv4acd *ll, struct ether_arp *arp) { + assert(ll); + assert(arp); + + /* see the BPF */ + if (memcmp(arp->arp_spa, &ll->address, sizeof(ll->address)) == 0) + return true; + + /* the TPA matched instead of the SPA, this is not a conflict */ + return false; +} + +static int ipv4acd_on_timeout(sd_event_source *s, uint64_t usec, void *userdata) { + sd_ipv4acd *ll = userdata; + int r = 0; + + assert(ll); + + switch (ll->state) { + case IPV4ACD_STATE_INIT: + + ipv4acd_set_state(ll, IPV4ACD_STATE_WAITING_PROBE, true); + + if (ll->conflict >= MAX_CONFLICTS) { + log_ipv4acd_notice(ll, "Max conflicts reached, delaying by %us", RATE_LIMIT_INTERVAL); + r = ipv4acd_set_next_wakeup(ll, RATE_LIMIT_INTERVAL, PROBE_WAIT); + if (r < 0) + goto out; + + ll->conflict = 0; + } else { + r = ipv4acd_set_next_wakeup(ll, 0, PROBE_WAIT); + if (r < 0) + goto out; + } + + break; + case IPV4ACD_STATE_WAITING_PROBE: + case IPV4ACD_STATE_PROBING: + /* Send a probe */ + r = arp_send_probe(ll->fd, ll->index, ll->address, &ll->mac_addr); + if (r < 0) { + log_ipv4acd_error_errno(ll, r, "Failed to send ARP probe: %m"); + goto out; + } else { + _cleanup_free_ char *address = NULL; + union in_addr_union addr = { .in.s_addr = ll->address }; + + r = in_addr_to_string(AF_INET, &addr, &address); + if (r >= 0) + log_ipv4acd_debug(ll, "Probing %s", address); + } + + if (ll->iteration < PROBE_NUM - 2) { + ipv4acd_set_state(ll, IPV4ACD_STATE_PROBING, false); + + r = ipv4acd_set_next_wakeup(ll, PROBE_MIN, (PROBE_MAX-PROBE_MIN)); + if (r < 0) + goto out; + } else { + ipv4acd_set_state(ll, IPV4ACD_STATE_WAITING_ANNOUNCE, true); + + r = ipv4acd_set_next_wakeup(ll, ANNOUNCE_WAIT, 0); + if (r < 0) + goto out; + } + + break; + + case IPV4ACD_STATE_ANNOUNCING: + if (ll->iteration >= ANNOUNCE_NUM - 1) { + ipv4acd_set_state(ll, IPV4ACD_STATE_RUNNING, false); + + break; + } + case IPV4ACD_STATE_WAITING_ANNOUNCE: + /* Send announcement packet */ + r = arp_send_announcement(ll->fd, ll->index, ll->address, &ll->mac_addr); + if (r < 0) { + log_ipv4acd_error_errno(ll, r, "Failed to send ARP announcement: %m"); + goto out; + } else + log_ipv4acd_debug(ll, "ANNOUNCE"); + + ipv4acd_set_state(ll, IPV4ACD_STATE_ANNOUNCING, false); + + r = ipv4acd_set_next_wakeup(ll, ANNOUNCE_INTERVAL, 0); + if (r < 0) + goto out; + + if (ll->iteration == 0) { + ll->conflict = 0; + ipv4acd_client_notify(ll, SD_IPV4ACD_EVENT_BIND); + } + + break; + default: + assert_not_reached("Invalid state."); + } + +out: + if (r < 0) + sd_ipv4acd_stop(ll); + + return 1; +} + +static void ipv4acd_on_conflict(sd_ipv4acd *ll) { + _cleanup_free_ char *address = NULL; + union in_addr_union addr = { .in.s_addr = ll->address }; + int r; + + assert(ll); + + ll->conflict++; + + r = in_addr_to_string(AF_INET, &addr, &address); + if (r >= 0) + log_ipv4acd_debug(ll, "Conflict on %s (%u)", address, ll->conflict); + + ipv4acd_stop(ll); + + ipv4acd_client_notify(ll, SD_IPV4ACD_EVENT_CONFLICT); +} + +static int ipv4acd_on_packet(sd_event_source *s, int fd, + uint32_t revents, void *userdata) { + sd_ipv4acd *ll = userdata; + struct ether_arp packet; + int r; + + assert(ll); + assert(fd >= 0); + + r = read(fd, &packet, sizeof(struct ether_arp)); + if (r < (int) sizeof(struct ether_arp)) + goto out; + + switch (ll->state) { + case IPV4ACD_STATE_ANNOUNCING: + case IPV4ACD_STATE_RUNNING: + if (ipv4acd_arp_conflict(ll, &packet)) { + usec_t ts; + + assert_se(sd_event_now(ll->event, clock_boottime_or_monotonic(), &ts) >= 0); + + /* Defend address */ + if (ts > ll->defend_window) { + ll->defend_window = ts + DEFEND_INTERVAL * USEC_PER_SEC; + r = arp_send_announcement(ll->fd, ll->index, ll->address, &ll->mac_addr); + if (r < 0) { + log_ipv4acd_error_errno(ll, r, "Failed to send ARP announcement: %m"); + goto out; + } else + log_ipv4acd_debug(ll, "DEFEND"); + + } else + ipv4acd_on_conflict(ll); + } + + break; + case IPV4ACD_STATE_WAITING_PROBE: + case IPV4ACD_STATE_PROBING: + case IPV4ACD_STATE_WAITING_ANNOUNCE: + /* BPF ensures this packet indicates a conflict */ + ipv4acd_on_conflict(ll); + + break; + default: + assert_not_reached("Invalid state."); + } + +out: + if (r < 0) + sd_ipv4acd_stop(ll); + + return 1; +} + +int sd_ipv4acd_set_index(sd_ipv4acd *ll, int interface_index) { + assert_return(ll, -EINVAL); + assert_return(interface_index > 0, -EINVAL); + assert_return(ll->state == IPV4ACD_STATE_INIT, -EBUSY); + + ll->index = interface_index; + + return 0; +} + +int sd_ipv4acd_set_mac(sd_ipv4acd *ll, const struct ether_addr *addr) { + assert_return(ll, -EINVAL); + assert_return(addr, -EINVAL); + assert_return(ll->state == IPV4ACD_STATE_INIT, -EBUSY); + + memcpy(&ll->mac_addr, addr, ETH_ALEN); + + return 0; +} + +int sd_ipv4acd_detach_event(sd_ipv4acd *ll) { + assert_return(ll, -EINVAL); + + ll->event = sd_event_unref(ll->event); + + return 0; +} + +int sd_ipv4acd_attach_event(sd_ipv4acd *ll, sd_event *event, int priority) { + int r; + + assert_return(ll, -EINVAL); + assert_return(!ll->event, -EBUSY); + + if (event) + ll->event = sd_event_ref(event); + else { + r = sd_event_default(&ll->event); + if (r < 0) + return r; + } + + ll->event_priority = priority; + + return 0; +} + +int sd_ipv4acd_set_callback(sd_ipv4acd *ll, sd_ipv4acd_cb_t cb, void *userdata) { + assert_return(ll, -EINVAL); + + ll->cb = cb; + ll->userdata = userdata; + + return 0; +} + +int sd_ipv4acd_set_address(sd_ipv4acd *ll, const struct in_addr *address){ + assert_return(ll, -EINVAL); + assert_return(address, -EINVAL); + assert_return(ll->state == IPV4ACD_STATE_INIT, -EBUSY); + + ll->address = address->s_addr; + + return 0; +} + +bool sd_ipv4acd_is_running(sd_ipv4acd *ll) { + assert_return(ll, false); + + return ll->state != IPV4ACD_STATE_INIT; +} + +static bool ether_addr_is_nul(const struct ether_addr *addr) { + const struct ether_addr nul_addr = {}; + + assert(addr); + + return memcmp(addr, &nul_addr, sizeof(struct ether_addr)) == 0; +} + +#define HASH_KEY SD_ID128_MAKE(df,04,22,98,3f,ad,14,52,f9,87,2e,d1,9c,70,e2,f2) + +int sd_ipv4acd_start(sd_ipv4acd *ll) { + int r; + + assert_return(ll, -EINVAL); + assert_return(ll->event, -EINVAL); + assert_return(ll->index > 0, -EINVAL); + assert_return(ll->address != 0, -EINVAL); + assert_return(!ether_addr_is_nul(&ll->mac_addr), -EINVAL); + assert_return(ll->state == IPV4ACD_STATE_INIT, -EBUSY); + + ll->defend_window = 0; + + r = arp_network_bind_raw_socket(ll->index, ll->address, &ll->mac_addr); + if (r < 0) + goto out; + + ll->fd = safe_close(ll->fd); + ll->fd = r; + + r = sd_event_add_io(ll->event, &ll->receive_message, ll->fd, + EPOLLIN, ipv4acd_on_packet, ll); + if (r < 0) + goto out; + + r = sd_event_source_set_priority(ll->receive_message, ll->event_priority); + if (r < 0) + goto out; + + r = sd_event_source_set_description(ll->receive_message, "ipv4acd-receive-message"); + if (r < 0) + goto out; + + r = ipv4acd_set_next_wakeup(ll, 0, 0); + if (r < 0) + goto out; +out: + if (r < 0) { + ipv4acd_stop(ll); + return r; + } + + return 0; +} diff --git a/src/libsystemd-network/sd-ipv4ll.c b/src/libsystemd-network/sd-ipv4ll.c index 14b9444dab..57bd337a9a 100644 --- a/src/libsystemd-network/sd-ipv4ll.c +++ b/src/libsystemd-network/sd-ipv4ll.c @@ -2,6 +2,7 @@ This file is part of systemd. Copyright (C) 2014 Axis Communications AB. All rights reserved. + Copyright (C) 2015 Tom Gundersen systemd is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by @@ -23,429 +24,154 @@ #include <stdio.h> #include <arpa/inet.h> -#include "util.h" -#include "siphash24.h" +#include "event-util.h" +#include "in-addr-util.h" #include "list.h" #include "random-util.h" +#include "refcnt.h" +#include "siphash24.h" +#include "sparse-endian.h" +#include "util.h" -#include "ipv4ll-internal.h" +#include "sd-ipv4acd.h" #include "sd-ipv4ll.h" -/* Constants from the RFC */ -#define PROBE_WAIT 1 -#define PROBE_NUM 3 -#define PROBE_MIN 1 -#define PROBE_MAX 2 -#define ANNOUNCE_WAIT 2 -#define ANNOUNCE_NUM 2 -#define ANNOUNCE_INTERVAL 2 -#define MAX_CONFLICTS 10 -#define RATE_LIMIT_INTERVAL 60 -#define DEFEND_INTERVAL 10 - #define IPV4LL_NETWORK 0xA9FE0000L #define IPV4LL_NETMASK 0xFFFF0000L -typedef enum IPv4LLTrigger{ - IPV4LL_TRIGGER_NULL, - IPV4LL_TRIGGER_PACKET, - IPV4LL_TRIGGER_TIMEOUT, - _IPV4LL_TRIGGER_MAX, - _IPV4LL_TRIGGER_INVALID = -1 -} IPv4LLTrigger; - -typedef enum IPv4LLState { - IPV4LL_STATE_INIT, - IPV4LL_STATE_WAITING_PROBE, - IPV4LL_STATE_PROBING, - IPV4LL_STATE_WAITING_ANNOUNCE, - IPV4LL_STATE_ANNOUNCING, - IPV4LL_STATE_RUNNING, - IPV4LL_STATE_STOPPED, - _IPV4LL_STATE_MAX, - _IPV4LL_STATE_INVALID = -1 -} IPv4LLState; +#define IPV4LL_DONT_DESTROY(ll) \ + _cleanup_ipv4ll_unref_ _unused_ sd_ipv4ll *_dont_destroy_##ll = sd_ipv4ll_ref(ll) struct sd_ipv4ll { unsigned n_ref; - IPv4LLState state; - int index; - int fd; - union sockaddr_union link; - int iteration; - int conflict; - sd_event_source *receive_message; - sd_event_source *timer; - usec_t next_wakeup; - usec_t defend_window; - int next_wakeup_valid; - be32_t address; + sd_ipv4acd *acd; + be32_t address; /* the address pushed to ACD */ struct random_data *random_data; char *random_data_state; + /* External */ be32_t claimed_address; - struct ether_addr mac_addr; - sd_event *event; - int event_priority; sd_ipv4ll_cb_t cb; void* userdata; }; -static void ipv4ll_run_state_machine(sd_ipv4ll *ll, IPv4LLTrigger trigger, void *trigger_data); - -static void ipv4ll_set_state(sd_ipv4ll *ll, IPv4LLState st, int reset_counter) { - - assert(ll); - assert(st < _IPV4LL_STATE_MAX); - - if (st == ll->state && !reset_counter) { - ll->iteration++; - } else { - ll->state = st; - ll->iteration = 0; - } -} - -static sd_ipv4ll *ipv4ll_client_notify(sd_ipv4ll *ll, int event) { - assert(ll); - - if (ll->cb) { - ll = sd_ipv4ll_ref(ll); - ll->cb(ll, event, ll->userdata); - ll = sd_ipv4ll_unref(ll); - } - - return ll; -} - -static sd_ipv4ll *ipv4ll_stop(sd_ipv4ll *ll, int event) { - assert(ll); - - ll->receive_message = sd_event_source_unref(ll->receive_message); - ll->fd = safe_close(ll->fd); - - ll->timer = sd_event_source_unref(ll->timer); - - log_ipv4ll(ll, "STOPPED"); - - ll = ipv4ll_client_notify(ll, event); +sd_ipv4ll *sd_ipv4ll_ref(sd_ipv4ll *ll) { + if (!ll) + return NULL; - if (ll) { - ll->claimed_address = 0; - ipv4ll_set_state (ll, IPV4LL_STATE_INIT, 1); - } + assert(ll->n_ref >= 1); + ll->n_ref++; return ll; } -static int ipv4ll_pick_address(sd_ipv4ll *ll, be32_t *address) { - be32_t addr; - int r; - int32_t random; - - assert(ll); - assert(address); - assert(ll->random_data); - - do { - r = random_r(ll->random_data, &random); - if (r < 0) - return r; - addr = htonl((random & 0x0000FFFF) | IPV4LL_NETWORK); - } while (addr == ll->address || - (ntohl(addr) & IPV4LL_NETMASK) != IPV4LL_NETWORK || - (ntohl(addr) & 0x0000FF00) == 0x0000 || - (ntohl(addr) & 0x0000FF00) == 0xFF00); - - *address = addr; - return 0; -} - -static int ipv4ll_timer(sd_event_source *s, uint64_t usec, void *userdata) { - sd_ipv4ll *ll = (sd_ipv4ll*)userdata; - - assert(ll); - - ll->next_wakeup_valid = 0; - ipv4ll_run_state_machine(ll, IPV4LL_TRIGGER_TIMEOUT, NULL); - - return 0; -} - -static void ipv4ll_set_next_wakeup(sd_ipv4ll *ll, int sec, int random_sec) { - usec_t next_timeout = 0; - usec_t time_now = 0; - - assert(sec >= 0); - assert(random_sec >= 0); - assert(ll); - - next_timeout = sec * USEC_PER_SEC; - - if (random_sec) - next_timeout += random_u32() % (random_sec * USEC_PER_SEC); - - assert_se(sd_event_now(ll->event, clock_boottime_or_monotonic(), &time_now) >= 0); - - ll->next_wakeup = time_now + next_timeout; - ll->next_wakeup_valid = 1; -} - -static bool ipv4ll_arp_conflict (sd_ipv4ll *ll, struct ether_arp *arp) { - assert(ll); - assert(arp); - - if (memcmp(arp->arp_spa, &ll->address, sizeof(ll->address)) == 0 && - memcmp(arp->arp_sha, &ll->mac_addr, ETH_ALEN) != 0) - return true; +sd_ipv4ll *sd_ipv4ll_unref(sd_ipv4ll *ll) { + if (!ll) + return NULL; - return false; -} + assert(ll->n_ref >= 1); + ll->n_ref--; -static bool ipv4ll_arp_probe_conflict (sd_ipv4ll *ll, struct ether_arp *arp) { - assert(ll); - assert(arp); + if (ll->n_ref > 0) + return NULL; - if (ipv4ll_arp_conflict(ll, arp)) - return true; + sd_ipv4acd_unref(ll->acd); - if (memcmp(arp->arp_tpa, &ll->address, sizeof(ll->address)) == 0 && - memcmp(arp->arp_sha, &ll->mac_addr, ETH_ALEN)) - return true; + free(ll->random_data); + free(ll->random_data_state); + free(ll); - return false; + return NULL; } -static void ipv4ll_run_state_machine(sd_ipv4ll *ll, IPv4LLTrigger trigger, void *trigger_data) { - struct ether_arp out_packet; - int out_packet_ready = 0; - int r = 0; - - assert(ll); - assert(trigger < _IPV4LL_TRIGGER_MAX); - - if (ll->state == IPV4LL_STATE_INIT) { - - log_ipv4ll(ll, "PROBE"); - ipv4ll_set_state(ll, IPV4LL_STATE_WAITING_PROBE, 1); - ipv4ll_set_next_wakeup(ll, 0, PROBE_WAIT); - - } else if ((ll->state == IPV4LL_STATE_WAITING_PROBE && trigger == IPV4LL_TRIGGER_TIMEOUT) || - (ll->state == IPV4LL_STATE_PROBING && trigger == IPV4LL_TRIGGER_TIMEOUT && ll->iteration < PROBE_NUM-2)) { - - /* Send a probe */ - arp_packet_probe(&out_packet, ll->address, &ll->mac_addr); - out_packet_ready = 1; - ipv4ll_set_state(ll, IPV4LL_STATE_PROBING, 0); - - ipv4ll_set_next_wakeup(ll, PROBE_MIN, (PROBE_MAX-PROBE_MIN)); - - } else if (ll->state == IPV4LL_STATE_PROBING && trigger == IPV4LL_TRIGGER_TIMEOUT && ll->iteration >= PROBE_NUM-2) { - - /* Send the last probe */ - arp_packet_probe(&out_packet, ll->address, &ll->mac_addr); - out_packet_ready = 1; - ipv4ll_set_state(ll, IPV4LL_STATE_WAITING_ANNOUNCE, 1); - - ipv4ll_set_next_wakeup(ll, ANNOUNCE_WAIT, 0); - - } else if ((ll->state == IPV4LL_STATE_WAITING_ANNOUNCE && trigger == IPV4LL_TRIGGER_TIMEOUT) || - (ll->state == IPV4LL_STATE_ANNOUNCING && trigger == IPV4LL_TRIGGER_TIMEOUT && ll->iteration < ANNOUNCE_NUM-1)) { - - /* Send announcement packet */ - arp_packet_announcement(&out_packet, ll->address, &ll->mac_addr); - out_packet_ready = 1; - ipv4ll_set_state(ll, IPV4LL_STATE_ANNOUNCING, 0); - - ipv4ll_set_next_wakeup(ll, ANNOUNCE_INTERVAL, 0); - - if (ll->iteration == 0) { - log_ipv4ll(ll, "ANNOUNCE"); - ll->claimed_address = ll->address; - ll = ipv4ll_client_notify(ll, IPV4LL_EVENT_BIND); - if (!ll || ll->state == IPV4LL_STATE_STOPPED) - goto out; - - ll->conflict = 0; - } - - } else if ((ll->state == IPV4LL_STATE_ANNOUNCING && trigger == IPV4LL_TRIGGER_TIMEOUT && - ll->iteration >= ANNOUNCE_NUM-1)) { - - ipv4ll_set_state(ll, IPV4LL_STATE_RUNNING, 0); - ll->next_wakeup_valid = 0; - - } else if (trigger == IPV4LL_TRIGGER_PACKET) { - - int conflicted = 0; - usec_t time_now; - struct ether_arp* in_packet = (struct ether_arp*)trigger_data; - - assert(in_packet); - - if (IN_SET(ll->state, IPV4LL_STATE_ANNOUNCING, IPV4LL_STATE_RUNNING)) { - - if (ipv4ll_arp_conflict(ll, in_packet)) { - - r = sd_event_now(ll->event, clock_boottime_or_monotonic(), &time_now); - if (r < 0) - goto out; - - /* Defend address */ - if (time_now > ll->defend_window) { - ll->defend_window = time_now + DEFEND_INTERVAL * USEC_PER_SEC; - arp_packet_announcement(&out_packet, ll->address, &ll->mac_addr); - out_packet_ready = 1; - } else - conflicted = 1; - } - - } else if (IN_SET(ll->state, IPV4LL_STATE_WAITING_PROBE, - IPV4LL_STATE_PROBING, - IPV4LL_STATE_WAITING_ANNOUNCE)) { - - conflicted = ipv4ll_arp_probe_conflict(ll, in_packet); - } - - if (conflicted) { - log_ipv4ll(ll, "CONFLICT"); - ll = ipv4ll_client_notify(ll, IPV4LL_EVENT_CONFLICT); - if (!ll || ll->state == IPV4LL_STATE_STOPPED) - goto out; +DEFINE_TRIVIAL_CLEANUP_FUNC(sd_ipv4ll*, sd_ipv4ll_unref); +#define _cleanup_ipv4ll_unref_ _cleanup_(sd_ipv4ll_unrefp) - ll->claimed_address = 0; +static void ipv4ll_on_acd(sd_ipv4acd *ll, int event, void *userdata); - /* Pick a new address */ - r = ipv4ll_pick_address(ll, &ll->address); - if (r < 0) - goto out; - ll->conflict++; - ll->defend_window = 0; - ipv4ll_set_state(ll, IPV4LL_STATE_WAITING_PROBE, 1); +int sd_ipv4ll_new(sd_ipv4ll **ret) { + _cleanup_ipv4ll_unref_ sd_ipv4ll *ll = NULL; + int r; - if (ll->conflict >= MAX_CONFLICTS) { - log_ipv4ll(ll, "MAX_CONFLICTS"); - ipv4ll_set_next_wakeup(ll, RATE_LIMIT_INTERVAL, PROBE_WAIT); - } else - ipv4ll_set_next_wakeup(ll, 0, PROBE_WAIT); + assert_return(ret, -EINVAL); - } - } + ll = new0(sd_ipv4ll, 1); + if (!ll) + return -ENOMEM; - if (out_packet_ready) { - r = arp_network_send_raw_socket(ll->fd, &ll->link, &out_packet); - if (r < 0) { - log_ipv4ll(ll, "failed to send arp packet out"); - goto out; - } - } + r = sd_ipv4acd_new(&ll->acd); + if (r < 0) + return r; - if (ll->next_wakeup_valid) { - ll->timer = sd_event_source_unref(ll->timer); - r = sd_event_add_time(ll->event, &ll->timer, clock_boottime_or_monotonic(), - ll->next_wakeup, 0, ipv4ll_timer, ll); - if (r < 0) - goto out; + r = sd_ipv4acd_set_callback(ll->acd, ipv4ll_on_acd, ll); + if (r < 0) + return r; - r = sd_event_source_set_priority(ll->timer, ll->event_priority); - if (r < 0) - goto out; + ll->n_ref = 1; - r = sd_event_source_set_description(ll->timer, "ipv4ll-timer"); - if (r < 0) - goto out; - } + *ret = ll; + ll = NULL; -out: - if (r < 0 && ll) - ipv4ll_stop(ll, r); + return 0; } -static int ipv4ll_receive_message(sd_event_source *s, int fd, - uint32_t revents, void *userdata) { +int sd_ipv4ll_stop(sd_ipv4ll *ll) { int r; - struct ether_arp arp; - sd_ipv4ll *ll = (sd_ipv4ll*)userdata; - - assert(ll); - r = read(fd, &arp, sizeof(struct ether_arp)); - if (r < (int) sizeof(struct ether_arp)) - return 0; + assert_return(ll, -EINVAL); - r = arp_packet_verify_headers(&arp); + r = sd_ipv4acd_stop(ll->acd); if (r < 0) - return 0; - - ipv4ll_run_state_machine(ll, IPV4LL_TRIGGER_PACKET, &arp); + return r; return 0; } int sd_ipv4ll_set_index(sd_ipv4ll *ll, int interface_index) { assert_return(ll, -EINVAL); - assert_return(interface_index > 0, -EINVAL); - assert_return(IN_SET(ll->state, IPV4LL_STATE_INIT, - IPV4LL_STATE_STOPPED), -EBUSY); - ll->index = interface_index; - - return 0; + return sd_ipv4acd_set_index(ll->acd, interface_index); } +#define HASH_KEY SD_ID128_MAKE(df,04,22,98,3f,ad,14,52,f9,87,2e,d1,9c,70,e2,f2) + int sd_ipv4ll_set_mac(sd_ipv4ll *ll, const struct ether_addr *addr) { - bool need_restart = false; + int r; assert_return(ll, -EINVAL); - assert_return(addr, -EINVAL); - - if (memcmp(&ll->mac_addr, addr, ETH_ALEN) == 0) - return 0; - if (!IN_SET(ll->state, IPV4LL_STATE_INIT, IPV4LL_STATE_STOPPED)) { - log_ipv4ll(ll, "Changing MAC address on running IPv4LL " - "client, restarting"); - ll = ipv4ll_stop(ll, IPV4LL_EVENT_STOP); - need_restart = true; - } + if (!ll->random_data) { + uint8_t seed[8]; - if (!ll) - return 0; + /* If no random data is set, generate some from the MAC */ + siphash24(seed, &addr->ether_addr_octet, + ETH_ALEN, HASH_KEY.bytes); - memcpy(&ll->mac_addr, addr, ETH_ALEN); + assert_cc(sizeof(unsigned) <= 8); - if (need_restart) - sd_ipv4ll_start(ll); + r = sd_ipv4ll_set_address_seed(ll, *(unsigned*)seed); + if (r < 0) + return r; + } - return 0; + return sd_ipv4acd_set_mac(ll->acd, addr); } int sd_ipv4ll_detach_event(sd_ipv4ll *ll) { assert_return(ll, -EINVAL); - ll->event = sd_event_unref(ll->event); - - return 0; + return sd_ipv4acd_detach_event(ll->acd); } int sd_ipv4ll_attach_event(sd_ipv4ll *ll, sd_event *event, int priority) { int r; assert_return(ll, -EINVAL); - assert_return(!ll->event, -EBUSY); - - if (event) - ll->event = sd_event_ref(event); - else { - r = sd_event_default(&ll->event); - if (r < 0) { - ipv4ll_stop(ll, IPV4LL_EVENT_STOP); - return r; - } - } - ll->event_priority = priority; + r = sd_ipv4acd_attach_event(ll->acd, event, priority); + if (r < 0) + return r; return 0; } @@ -467,189 +193,176 @@ int sd_ipv4ll_get_address(sd_ipv4ll *ll, struct in_addr *address){ return -ENOENT; address->s_addr = ll->claimed_address; + return 0; } -int sd_ipv4ll_set_address_seed (sd_ipv4ll *ll, uint8_t seed[8]) { - unsigned int entropy; +int sd_ipv4ll_set_address_seed(sd_ipv4ll *ll, unsigned seed) { + _cleanup_free_ struct random_data *random_data = NULL; + _cleanup_free_ char *random_data_state = NULL; int r; assert_return(ll, -EINVAL); - assert_return(seed, -EINVAL); - entropy = *seed; + random_data = new0(struct random_data, 1); + if (!random_data) + return -ENOMEM; - free(ll->random_data); - free(ll->random_data_state); + random_data_state = new0(char, 128); + if (!random_data_state) + return -ENOMEM; - ll->random_data = new0(struct random_data, 1); - ll->random_data_state = new0(char, 128); + r = initstate_r(seed, random_data_state, 128, random_data); + if (r < 0) + return r; - if (!ll->random_data || !ll->random_data_state) { - r = -ENOMEM; - goto error; - } + free(ll->random_data); + ll->random_data = random_data; + random_data = NULL; - r = initstate_r((unsigned int)entropy, ll->random_data_state, 128, ll->random_data); - if (r < 0) - goto error; + free(ll->random_data_state); + ll->random_data_state = random_data_state; + random_data_state = NULL; -error: - if (r < 0){ - free(ll->random_data); - free(ll->random_data_state); - ll->random_data = NULL; - ll->random_data_state = NULL; - } - return r; + return 0; } bool sd_ipv4ll_is_running(sd_ipv4ll *ll) { assert_return(ll, false); - return !IN_SET(ll->state, IPV4LL_STATE_INIT, IPV4LL_STATE_STOPPED); + return sd_ipv4acd_is_running(ll->acd); } -#define HASH_KEY SD_ID128_MAKE(df,04,22,98,3f,ad,14,52,f9,87,2e,d1,9c,70,e2,f2) - -int sd_ipv4ll_start (sd_ipv4ll *ll) { - int r; +static bool ipv4ll_address_is_valid(const struct in_addr *address) { + uint32_t addr; - assert_return(ll, -EINVAL); - assert_return(ll->event, -EINVAL); - assert_return(ll->index > 0, -EINVAL); - assert_return(IN_SET(ll->state, IPV4LL_STATE_INIT, - IPV4LL_STATE_STOPPED), -EBUSY); - - ll->state = IPV4LL_STATE_INIT; - - r = arp_network_bind_raw_socket(ll->index, &ll->link); - - if (r < 0) - goto out; + assert(address); - ll->fd = r; - ll->conflict = 0; - ll->defend_window = 0; - ll->claimed_address = 0; + if (!in_addr_is_link_local(AF_INET, (const union in_addr_union *) address)) + return false; - if (!ll->random_data) { - uint8_t seed[8]; + addr = be32toh(address->s_addr); - /* Fallback to mac */ - siphash24(seed, &ll->mac_addr.ether_addr_octet, - ETH_ALEN, HASH_KEY.bytes); + if ((addr & 0x0000FF00) == 0x0000 || + (addr & 0x0000FF00) == 0xFF00) + return false; - r = sd_ipv4ll_set_address_seed(ll, seed); - if (r < 0) - goto out; - } + return true; +} - if (ll->address == 0) { - r = ipv4ll_pick_address(ll, &ll->address); - if (r < 0) - goto out; - } +int sd_ipv4ll_set_address(sd_ipv4ll *ll, const struct in_addr *address) { + int r; - ipv4ll_set_state (ll, IPV4LL_STATE_INIT, 1); + assert_return(ll, -EINVAL); + assert_return(address, -EINVAL); + assert_return(ipv4ll_address_is_valid(address), -EINVAL); - r = sd_event_add_io(ll->event, &ll->receive_message, ll->fd, - EPOLLIN, ipv4ll_receive_message, ll); + r = sd_ipv4acd_set_address(ll->acd, address); if (r < 0) - goto out; + return r; - r = sd_event_source_set_priority(ll->receive_message, ll->event_priority); - if (r < 0) - goto out; + ll->address = address->s_addr; - r = sd_event_source_set_description(ll->receive_message, "ipv4ll-receive-message"); - if (r < 0) - goto out; + return 0; +} + +static int ipv4ll_pick_address(sd_ipv4ll *ll) { + struct in_addr in_addr; + be32_t addr; + int r; + int32_t random; - r = sd_event_add_time(ll->event, - &ll->timer, - clock_boottime_or_monotonic(), - now(clock_boottime_or_monotonic()), 0, - ipv4ll_timer, ll); + assert(ll); + assert(ll->random_data); - if (r < 0) - goto out; + 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) & 0x0000FF00) == 0x0000 || + (ntohl(addr) & 0x0000FF00) == 0xFF00); - r = sd_event_source_set_priority(ll->timer, ll->event_priority); - if (r < 0) - goto out; + in_addr.s_addr = addr; - r = sd_event_source_set_description(ll->timer, "ipv4ll-timer"); -out: + r = sd_ipv4ll_set_address(ll, &in_addr); if (r < 0) - ipv4ll_stop(ll, IPV4LL_EVENT_STOP); + return r; return 0; } -int sd_ipv4ll_stop(sd_ipv4ll *ll) { - ipv4ll_stop(ll, IPV4LL_EVENT_STOP); - if (ll) - ipv4ll_set_state(ll, IPV4LL_STATE_STOPPED, 1); - - return 0; -} +int sd_ipv4ll_start(sd_ipv4ll *ll) { + int r; -sd_ipv4ll *sd_ipv4ll_ref(sd_ipv4ll *ll) { + assert_return(ll, -EINVAL); + assert_return(ll->random_data, -EINVAL); - if (!ll) - return NULL; + if (ll->address == 0) { + r = ipv4ll_pick_address(ll); + if (r < 0) + return r; + } - assert(ll->n_ref >= 1); - ll->n_ref++; + r = sd_ipv4acd_start(ll->acd); + if (r < 0) + return r; - return ll; + return 0; } -sd_ipv4ll *sd_ipv4ll_unref(sd_ipv4ll *ll) { - - if (!ll) - return NULL; - - assert(ll->n_ref >= 1); - ll->n_ref--; +static void ipv4ll_client_notify(sd_ipv4ll *ll, int event) { + assert(ll); - if (ll->n_ref > 0) - return ll; + if (ll->cb) + ll->cb(ll, event, ll->userdata); +} - ll->receive_message = sd_event_source_unref(ll->receive_message); - ll->fd = safe_close(ll->fd); +void ipv4ll_on_acd(sd_ipv4acd *acd, int event, void *userdata) { + sd_ipv4ll *ll = userdata; + IPV4LL_DONT_DESTROY(ll); + int r; - ll->timer = sd_event_source_unref(ll->timer); + assert(acd); + assert(ll); - sd_ipv4ll_detach_event(ll); + switch (event) { + case SD_IPV4ACD_EVENT_STOP: + ipv4ll_client_notify(ll, SD_IPV4LL_EVENT_STOP); - free(ll->random_data); - free(ll->random_data_state); - free(ll); + ll->claimed_address = 0; - return NULL; -} + break; + case SD_IPV4ACD_EVENT_BIND: + ll->claimed_address = ll->address; + ipv4ll_client_notify(ll, SD_IPV4LL_EVENT_BIND); -DEFINE_TRIVIAL_CLEANUP_FUNC(sd_ipv4ll*, sd_ipv4ll_unref); -#define _cleanup_ipv4ll_free_ _cleanup_(sd_ipv4ll_unrefp) + break; + 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; - - assert_return(ret, -EINVAL); + ll->claimed_address = 0; + } else { + r = ipv4ll_pick_address(ll); + if (r < 0) + goto error; - ll = new0(sd_ipv4ll, 1); - if (!ll) - return -ENOMEM; + r = sd_ipv4acd_start(ll->acd); + if (r < 0) + goto error; + } - ll->n_ref = 1; - ll->state = IPV4LL_STATE_INIT; - ll->index = -1; - ll->fd = -1; + break; + default: + assert_not_reached("Invalid IPv4ACD event."); + } - *ret = ll; - ll = NULL; + return; - return 0; +error: + ipv4ll_client_notify(ll, SD_IPV4LL_EVENT_STOP); } diff --git a/src/libsystemd-network/sd-lldp.c b/src/libsystemd-network/sd-lldp.c index 574e04b541..06949a1e83 100644 --- a/src/libsystemd-network/sd-lldp.c +++ b/src/libsystemd-network/sd-lldp.c @@ -68,16 +68,14 @@ struct sd_lldp { 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; +static void chassis_id_hash_func(const void *p, struct siphash *state) { const lldp_chassis_id *id = p; assert(id); + 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); } static int chassis_id_compare_func(const void *_a, const void *_b) { @@ -199,7 +197,7 @@ int lldp_handle_packet(tlv_packet *tlv, uint16_t length) { goto out; } - /* skip type and lengh encoding */ + /* skip type and length encoding */ p += 2; q = p; @@ -338,7 +336,7 @@ int lldp_handle_packet(tlv_packet *tlv, uint16_t length) { lldp->statistics.stats_frames_in_errors_total ++; } - tlv_packet_free(tlv); + sd_lldp_packet_unref(tlv); return 0; } @@ -366,10 +364,16 @@ static void lldp_set_state(sd_lldp *lldp, LLDPAgentRXState state) { } static void lldp_run_state_machine(sd_lldp *lldp) { + if (!lldp->cb) + return; - if (lldp->rx_state == LLDP_AGENT_RX_UPDATE_INFO) - if (lldp->cb) - lldp->cb(lldp, LLDP_AGENT_RX_UPDATE_INFO, lldp->userdata); + switch (lldp->rx_state) { + case LLDP_AGENT_RX_UPDATE_INFO: + lldp->cb(lldp, SD_LLDP_EVENT_UPDATE_INFO, lldp->userdata); + break; + default: + break; + } } /* 10.5.5.2.1 mibDeleteObjects () @@ -449,7 +453,7 @@ int sd_lldp_save(sd_lldp *lldp, const char *lldp_file) { _cleanup_free_ char *s = NULL; char *k, *t; - r = lldp_read_chassis_id(p->packet, &type, &length, &mac); + r = sd_lldp_packet_read_chassis_id(p->packet, &type, &mac, &length); if (r < 0) continue; @@ -462,7 +466,7 @@ int sd_lldp_save(sd_lldp *lldp, const char *lldp_file) { goto fail; } - r = lldp_read_port_id(p->packet, &type, &length, &port_id); + r = sd_lldp_packet_read_port_id(p->packet, &type, &port_id, &length); if (r < 0) continue; @@ -507,7 +511,7 @@ int sd_lldp_save(sd_lldp *lldp, const char *lldp_file) { free(s); s = k; - r = lldp_read_system_name(p->packet, &length, &k); + r = sd_lldp_packet_read_system_name(p->packet, &k, &length); if (r < 0) k = strappend(s, "'_NAME=N/A' "); else { @@ -529,7 +533,7 @@ int sd_lldp_save(sd_lldp *lldp, const char *lldp_file) { free(s); s = k; - (void) lldp_read_system_capability(p->packet, &data); + (void) sd_lldp_packet_read_system_capability(p->packet, &data); sprintf(buf, "'_CAP=%x'", data); @@ -696,3 +700,35 @@ int sd_lldp_new(int ifindex, return 0; } + +int sd_lldp_get_packets(sd_lldp *lldp, sd_lldp_packet ***tlvs) { + lldp_neighbour_port *p; + lldp_chassis *c; + Iterator iter; + unsigned count = 0, i; + + assert_return(lldp, -EINVAL); + assert_return(tlvs, -EINVAL); + + HASHMAP_FOREACH(c, lldp->neighbour_mib, iter) { + LIST_FOREACH(port, p, c->ports) + count++; + } + + if (!count) { + *tlvs = NULL; + return 0; + } + + *tlvs = new(sd_lldp_packet *, count); + if (!*tlvs) + return -ENOMEM; + + i = 0; + HASHMAP_FOREACH(c, lldp->neighbour_mib, iter) { + LIST_FOREACH(port, p, c->ports) + (*tlvs)[i++] = sd_lldp_packet_ref(p->packet); + } + + return count; +} diff --git a/src/libsystemd-network/sd-pppoe.c b/src/libsystemd-network/sd-pppoe.c index c6c9da812b..cd5a204f8c 100644 --- a/src/libsystemd-network/sd-pppoe.c +++ b/src/libsystemd-network/sd-pppoe.c @@ -385,7 +385,7 @@ static int pppoe_send_initiation(sd_pppoe *ppp) { return r; log_debug("PPPoE: sent DISCOVER (Service-Name: %s)", - ppp->service_name ? : ""); + strna(ppp->service_name)); pppoe_arm_timeout(ppp); @@ -625,8 +625,8 @@ static int pppoe_handle_message(sd_pppoe *ppp, struct pppoe_hdr *packet, struct mac->ether_addr_octet[3], mac->ether_addr_octet[4], mac->ether_addr_octet[5], - ppp->tags.service_name ? : "", - ppp->tags.ac_name ? : ""); + strempty(ppp->tags.service_name), + strempty(ppp->tags.ac_name)); memcpy(&ppp->peer_mac, mac, ETH_ALEN); @@ -670,7 +670,7 @@ static int pppoe_handle_message(sd_pppoe *ppp, struct pppoe_hdr *packet, struct ppp->timeout = sd_event_source_unref(ppp->timeout); assert(ppp->cb); - ppp->cb(ppp, PPPOE_EVENT_RUNNING, ppp->userdata); + ppp->cb(ppp, SD_PPPOE_EVENT_RUNNING, ppp->userdata); break; case PPPOE_STATE_RUNNING: @@ -688,7 +688,7 @@ static int pppoe_handle_message(sd_pppoe *ppp, struct pppoe_hdr *packet, struct ppp->state = PPPOE_STATE_STOPPED; assert(ppp->cb); - ppp->cb(ppp, PPPOE_EVENT_STOPPED, ppp->userdata); + ppp->cb(ppp, SD_PPPOE_EVENT_STOPPED, ppp->userdata); break; case PPPOE_STATE_STOPPED: diff --git a/src/libsystemd-network/test-acd.c b/src/libsystemd-network/test-acd.c new file mode 100644 index 0000000000..94c31af3f3 --- /dev/null +++ b/src/libsystemd-network/test-acd.c @@ -0,0 +1,117 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright (C) 2014 Tom Gundersen <teg@jklm.no> + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see <http://www.gnu.org/licenses/>. +***/ + +#include <stdlib.h> +#include <errno.h> +#include <unistd.h> + +#include <linux/veth.h> +#include <net/if.h> + +#include "sd-event.h" +#include "sd-netlink.h" +#include "sd-ipv4acd.h" + +#include "util.h" +#include "event-util.h" +#include "netlink-util.h" +#include "in-addr-util.h" + +static void acd_handler(sd_ipv4acd *acd, int event, void *userdata) { + assert_se(acd); + + switch (event) { + case 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_index(acd, ifindex) >= 0); + assert_se(sd_ipv4acd_set_mac(acd, ha) >= 0); + assert_se(sd_ipv4acd_set_address(acd, pa) >= 0); + assert_se(sd_ipv4acd_set_callback(acd, acd_handler, NULL) >= 0); + + log_info("starting IPv4ACD client"); + + assert_se(sd_ipv4acd_start(acd) >= 0); + + assert_se(sd_event_loop(e) >= 0); + + assert_se(!sd_ipv4acd_unref(acd)); + + return EXIT_SUCCESS; +} + +static int test_acd(const char *ifname, const char *address) { + _cleanup_event_unref_ sd_event *e = NULL; + _cleanup_netlink_unref_ sd_netlink *rtnl = NULL; + _cleanup_netlink_message_unref_ sd_netlink_message *m = NULL, *reply = NULL; + union in_addr_union pa; + struct ether_addr ha; + int ifindex; + + assert_se(in_addr_from_string(AF_INET, address, &pa) >= 0); + + assert_se(sd_event_new(&e) >= 0); + + assert_se(sd_netlink_open(&rtnl) >= 0); + assert_se(sd_netlink_attach_event(rtnl, e, 0) >= 0); + + assert_se(sd_rtnl_message_new_link(rtnl, &m, RTM_GETLINK, 0) >= 0); + assert_se(sd_netlink_message_append_string(m, IFLA_IFNAME, ifname) >= 0); + assert_se(sd_netlink_call(rtnl, m, 0, &reply) >= 0); + + assert_se(sd_rtnl_message_link_get_ifindex(reply, &ifindex) >= 0); + assert_se(sd_netlink_message_read_ether_addr(reply, IFLA_ADDRESS, &ha) >= 0); + + client_run(ifindex, &pa.in, &ha, e); + + return EXIT_SUCCESS; +} + +int main(int argc, char *argv[]) { + log_set_max_level(LOG_DEBUG); + log_parse_environment(); + log_open(); + + if (argc == 3) + return test_acd(argv[1], argv[2]); + else { + log_error("This program takes two arguments.\n" + "\t %s <ifname> <IPv4 address>", program_invocation_short_name); + return EXIT_FAILURE; + } +} diff --git a/src/libsystemd-network/test-dhcp-client.c b/src/libsystemd-network/test-dhcp-client.c index 29c20b77e3..c112ec8134 100644 --- a/src/libsystemd-network/test-dhcp-client.c +++ b/src/libsystemd-network/test-dhcp-client.c @@ -360,7 +360,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); diff --git a/src/libsystemd-network/test-dhcp-server.c b/src/libsystemd-network/test-dhcp-server.c index 7d8a1f6bd9..c3bcb9cb4b 100644 --- a/src/libsystemd-network/test-dhcp-server.c +++ b/src/libsystemd-network/test-dhcp-server.c @@ -198,6 +198,17 @@ 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; + uint64_t hash; + + siphash24_init(&state, key); + client_id_hash_func(id, &state); + siphash24_finalize((uint8_t*)&hash, &state); + + return hash; +} + static void test_client_id_hash(void) { DHCPClientId a = { .length = 4, @@ -213,18 +224,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"); diff --git a/src/libsystemd-network/test-dhcp6-client.c b/src/libsystemd-network/test-dhcp6-client.c index 9c7f9ffb1b..0c131a9897 100644 --- a/src/libsystemd-network/test-dhcp6-client.c +++ b/src/libsystemd-network/test-dhcp6-client.c @@ -365,7 +365,7 @@ static void test_client_solicit_cb(sd_dhcp6_client *client, int event, 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); @@ -564,7 +564,7 @@ static void test_client_information_cb(sd_dhcp6_client *client, int event, 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); @@ -581,7 +581,11 @@ static void test_client_information_cb(sd_dhcp6_client *client, int event, 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); diff --git a/src/libsystemd-network/test-icmp6-rs.c b/src/libsystemd-network/test-icmp6-rs.c index 8ba21106a7..27b0ef4572 100644 --- a/src/libsystemd-network/test-icmp6-rs.c +++ b/src/libsystemd-network/test-icmp6-rs.c @@ -277,9 +277,9 @@ static void test_rs_done(sd_icmp6_nd *nd, int event, void *userdata) { 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 } + { 0, SD_ICMP6_ND_EVENT_ROUTER_ADVERTISMENT_NONE }, + { ND_RA_FLAG_OTHER, SD_ICMP6_ND_EVENT_ROUTER_ADVERTISMENT_OTHER }, + { ND_RA_FLAG_MANAGED, SD_ICMP6_ND_EVENT_ROUTER_ADVERTISMENT_MANAGED } }; uint32_t mtu; diff --git a/src/libsystemd-network/test-ipv4ll-manual.c b/src/libsystemd-network/test-ipv4ll-manual.c new file mode 100644 index 0000000000..dd2e44e7a3 --- /dev/null +++ b/src/libsystemd-network/test-ipv4ll-manual.c @@ -0,0 +1,129 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright (C) 2014 Tom Gundersen <teg@jklm.no> + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see <http://www.gnu.org/licenses/>. +***/ + +#include <stdlib.h> +#include <errno.h> +#include <unistd.h> + +#include <linux/veth.h> +#include <net/if.h> + +#include "sd-event.h" +#include "sd-netlink.h" +#include "sd-ipv4ll.h" + +#include "util.h" +#include "event-util.h" +#include "netlink-util.h" +#include "in-addr-util.h" + +static void ll_handler(sd_ipv4ll *ll, int event, void *userdata) { + _cleanup_free_ char *address = NULL; + struct in_addr addr = {}; + + assert_se(ll); + + if (sd_ipv4ll_get_address(ll, &addr) >= 0) + assert_se(in_addr_to_string(AF_INET, (const union in_addr_union*) &addr, &address) >= 0); + + switch (event) { + case 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_index(ll, ifindex) >= 0); + assert_se(sd_ipv4ll_set_mac(ll, ha) >= 0); + assert_se(sd_ipv4ll_set_callback(ll, ll_handler, NULL) >= 0); + + if (seed_str) { + unsigned seed; + + assert_se(safe_atou(seed_str, &seed) >= 0); + + assert_se(sd_ipv4ll_set_address_seed(ll, seed) >= 0); + } + + log_info("starting IPv4LL client"); + + assert_se(sd_ipv4ll_start(ll) >= 0); + + assert_se(sd_event_loop(e) >= 0); + + assert_se(!sd_ipv4ll_unref(ll)); + + return EXIT_SUCCESS; +} + +static int test_ll(const char *ifname, const char *seed) { + _cleanup_event_unref_ sd_event *e = NULL; + _cleanup_netlink_unref_ sd_netlink *rtnl = NULL; + _cleanup_netlink_message_unref_ sd_netlink_message *m = NULL, *reply = NULL; + struct ether_addr ha; + int ifindex; + + assert_se(sd_event_new(&e) >= 0); + + assert_se(sd_netlink_open(&rtnl) >= 0); + assert_se(sd_netlink_attach_event(rtnl, e, 0) >= 0); + + assert_se(sd_rtnl_message_new_link(rtnl, &m, RTM_GETLINK, 0) >= 0); + assert_se(sd_netlink_message_append_string(m, IFLA_IFNAME, ifname) >= 0); + assert_se(sd_netlink_call(rtnl, m, 0, &reply) >= 0); + + assert_se(sd_rtnl_message_link_get_ifindex(reply, &ifindex) >= 0); + assert_se(sd_netlink_message_read_ether_addr(reply, IFLA_ADDRESS, &ha) >= 0); + + client_run(ifindex, seed, &ha, e); + + return EXIT_SUCCESS; +} + +int main(int argc, char *argv[]) { + log_set_max_level(LOG_DEBUG); + log_parse_environment(); + log_open(); + + if (argc == 2) + return test_ll(argv[1], NULL); + else if (argc == 3) + return test_ll(argv[1], argv[2]); + else { + log_error("This program takes one or two arguments.\n" + "\t %s <ifname> [<seed>]", program_invocation_short_name); + return EXIT_FAILURE; + } +} diff --git a/src/libsystemd-network/test-ipv4ll.c b/src/libsystemd-network/test-ipv4ll.c index d60ee98b25..b67a9f17d7 100644 --- a/src/libsystemd-network/test-ipv4ll.c +++ b/src/libsystemd-network/test-ipv4ll.c @@ -31,7 +31,7 @@ #include "event-util.h" #include "sd-ipv4ll.h" -#include "ipv4ll-internal.h" +#include "arp-util.h" static bool verbose = false; static bool extended = false; @@ -44,10 +44,10 @@ static void basic_request_handler(sd_ipv4ll *ll, int event, void *userdata) { 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 = {}; + unsigned seed = 0; sd_ipv4ll *ll; struct ether_addr mac_addr = { .ether_addr_octet = {'A', 'B', 'C', '1', '2', '3'}}; @@ -134,8 +119,17 @@ 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); @@ -149,7 +143,7 @@ static void test_public_api_setters(sd_event *e) { assert_se(sd_ipv4ll_set_index(ll, 99) == 0); assert_se(sd_ipv4ll_ref(ll) == ll); - assert_se(sd_ipv4ll_unref(ll) == ll); + assert_se(sd_ipv4ll_unref(ll) == NULL); /* Cleanup */ assert_se(sd_ipv4ll_unref(ll) == NULL); @@ -184,21 +178,20 @@ static void test_basic_request(sd_event *e) { sd_event_run(e, (uint64_t) -1); assert_se(sd_ipv4ll_start(ll) == -EBUSY); + assert_se(sd_ipv4ll_is_running(ll)); + /* PROBE */ sd_event_run(e, (uint64_t) -1); assert_se(read(test_fd[1], &arp, sizeof(struct ether_arp)) == sizeof(struct ether_arp)); - test_arp_header(&arp); if (extended) { /* PROBE */ sd_event_run(e, (uint64_t) -1); assert_se(read(test_fd[1], &arp, sizeof(struct ether_arp)) == sizeof(struct ether_arp)); - test_arp_header(&arp); /* PROBE */ sd_event_run(e, (uint64_t) -1); assert_se(read(test_fd[1], &arp, sizeof(struct ether_arp)) == sizeof(struct ether_arp)); - test_arp_header(&arp); sd_event_run(e, (uint64_t) -1); assert_se(basic_request_handler_bind == 1); @@ -215,11 +208,13 @@ static void test_basic_request(sd_event *e) { int main(int argc, char *argv[]) { _cleanup_event_unref_ sd_event *e = NULL; + log_set_max_level(LOG_DEBUG); + log_parse_environment(); + log_open(); + assert_se(sd_event_new(&e) >= 0); test_public_api_setters(e); - test_arp_probe(); - test_arp_announce(); test_basic_request(e); return 0; diff --git a/src/libsystemd-network/test-lldp.c b/src/libsystemd-network/test-lldp.c index 06545aee59..e57102a576 100644 --- a/src/libsystemd-network/test-lldp.c +++ b/src/libsystemd-network/test-lldp.c @@ -25,20 +25,26 @@ #include <net/ethernet.h> #include <arpa/inet.h> +#include "sd-lldp.h" +#include "sd-event.h" +#include "event-util.h" #include "macro.h" #include "lldp.h" #include "lldp-tlv.h" +#include "lldp-network.h" #define TEST_LLDP_PORT "em1" #define TEST_LLDP_TYPE_SYSTEM_NAME "systemd-lldp" #define TEST_LLDP_TYPE_SYSTEM_DESC "systemd-lldp-desc" +static int test_fd[2]; + 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; + _cleanup_lldp_packet_unref_ tlv_packet *m = NULL; const uint8_t lldp_dst[] = LLDP_MULTICAST_ADDR; struct ether_header ether = { .ether_type = htons(ETHERTYPE_LLDP), @@ -202,6 +208,15 @@ static int lldp_parse_ttl_tlv(tlv_packet *m) { return 0; } +static int lldp_get_destination_type(tlv_packet *m) { + int dest; + + assert_se(sd_lldp_packet_get_destination_type(m, &dest) >= 0); + assert_se(dest == SD_LLDP_DESTINATION_TYPE_NEAREST_BRIDGE); + + return 0; +} + static int lldp_parse_tlv_packet(tlv_packet *m, int len) { uint8_t subtype; @@ -212,20 +227,241 @@ static int lldp_parse_tlv_packet(tlv_packet *m, int len) { assert_se(lldp_parse_ttl_tlv(m) >= 0); assert_se(lldp_parse_system_desc_tlv(m) >= 0); + assert_se(lldp_get_destination_type(m) >= 0); + return 0; } -int main(int argc, char *argv[]) { - _cleanup_tlv_packet_free_ tlv_packet *tlv = NULL; +static void test_parser(void) { + _cleanup_lldp_packet_unref_ tlv_packet *tlv = NULL; /* form a packet */ lldp_build_tlv_packet(&tlv); - /* parse the packet */ tlv_packet_parse_pdu(tlv, tlv->length); - /* verify */ lldp_parse_tlv_packet(tlv, tlv->length); +} + +int lldp_network_bind_raw_socket(int ifindex) { + if (socketpair(AF_UNIX, SOCK_DGRAM | SOCK_NONBLOCK, 0, test_fd) < 0) + return -errno; + + return test_fd[0]; +} + +static int lldp_handler_calls; +static void lldp_handler (sd_lldp *lldp, int event, void *userdata) { + lldp_handler_calls++; +} + +static int start_lldp(sd_lldp **lldp, sd_event *e, sd_lldp_cb_t cb, void *cb_data) { + int r; + + r = sd_lldp_new(42, "dummy", &mac_addr, lldp); + if (r) + return r; + + r = sd_lldp_attach_event(*lldp, e, 0); + if (r) + return r; + + r = sd_lldp_set_callback(*lldp, cb, cb_data); + if (r) + return r; + + r = sd_lldp_start(*lldp); + if (r) + return r; + + return 0; +} + +static int stop_lldp(sd_lldp *lldp) { + int r; + + r = sd_lldp_stop(lldp); + if (r) + return r; + + r = sd_lldp_detach_event(lldp); + if (r) + return r; + + sd_lldp_free(lldp); + safe_close(test_fd[1]); + + return 0; +} + +static void test_receive_basic_packet(sd_event *e) { + sd_lldp *lldp; + sd_lldp_packet **packets; + uint8_t type, *data; + uint16_t length, ttl; + int dest_type; + char *str; + 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 */ + }; + + 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_packets(lldp, &packets) == 1); + + assert_se(sd_lldp_packet_read_chassis_id(packets[0], &type, &data, &length) == 0); + assert_se(type == LLDP_CHASSIS_SUBTYPE_MAC_ADDRESS); + assert_se(length == ETH_ALEN); + assert_se(!memcmp(data, "\x00\x01\x02\x03\x04\x05", ETH_ALEN)); + + assert_se(sd_lldp_packet_read_port_id(packets[0], &type, &data, &length) == 0); + assert_se(type == LLDP_PORT_SUBTYPE_INTERFACE_NAME); + assert_se(length == 3); + assert_se(strneq((char *) data, "1/3", 3)); + + assert_se(sd_lldp_packet_read_port_description(packets[0], &str, &length) == 0); + assert_se(length == 4); + assert_se(strneq(str, "Port", 4)); + + assert_se(sd_lldp_packet_read_system_name(packets[0], &str, &length) == 0); + assert_se(length == 3); + assert_se(strneq(str, "SYS", 3)); + + assert_se(sd_lldp_packet_read_system_description(packets[0], &str, &length) == 0); + assert_se(length == 4); /* This is the real length in the TLV packet */ + assert_se(strneq(str, "foo", 3)); + + assert_se(sd_lldp_packet_read_ttl(packets[0], &ttl) == 0); + assert_se(ttl == 120); + + assert_se(sd_lldp_packet_get_destination_type(packets[0], &dest_type) == 0); + assert_se(dest_type == SD_LLDP_DESTINATION_TYPE_NEAREST_NON_TPMR_BRIDGE); + + sd_lldp_packet_unref(packets[0]); + free(packets); + + assert_se(stop_lldp(lldp) == 0); +} + +static void test_receive_incomplete_packet(sd_event *e) { + sd_lldp *lldp; + sd_lldp_packet **packets; + 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 */ + }; + + 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 == 0); + assert_se(sd_lldp_get_packets(lldp, &packets) == 0); + + assert_se(stop_lldp(lldp) == 0); +} + +static void test_receive_oui_packet(sd_event *e) { + sd_lldp *lldp; + sd_lldp_packet **packets; + uint32_t id32; + uint16_t id16, len; + uint8_t flags; + char *str; + 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 */ + }; + + 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_packets(lldp, &packets) == 1); + + assert_se(sd_lldp_packet_read_port_vlan_id(packets[0], &id16) == 0); + assert_se(id16 == 0x1234); + + assert_se(sd_lldp_packet_read_port_protocol_vlan_id(packets[0], &flags, &id16) == 0); + assert_se(flags == 1); + assert_se(id16 == 0x7788); + + assert_se(sd_lldp_packet_read_vlan_name(packets[0], &id16, &str, &len) == 0); + assert_se(id16 == 0x1234); + assert_se(len == 6); + assert_se(strneq(str, "Vlan51", 6)); + + assert_se(sd_lldp_packet_read_management_vid(packets[0], &id16) == 0); + assert_se(id16 == 0x0102); + + assert_se(sd_lldp_packet_read_link_aggregation(packets[0], &flags, &id32) == 0); + assert_se(flags == 1); + assert_se(id32 == 0x00140012); + + sd_lldp_packet_unref(packets[0]); + free(packets); + + assert_se(stop_lldp(lldp) == 0); +} + +int main(int argc, char *argv[]) { + _cleanup_event_unref_ sd_event *e = NULL; + + test_parser(); + + /* 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-pppoe.c b/src/libsystemd-network/test-pppoe.c index 72878f4b51..6ea460d9ac 100644 --- a/src/libsystemd-network/test-pppoe.c +++ b/src/libsystemd-network/test-pppoe.c @@ -19,19 +19,20 @@ 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 <stdlib.h> +#include <unistd.h> +#include <sched.h> -#include "util.h" #include "sd-event.h" -#include "event-util.h" #include "sd-netlink.h" #include "sd-pppoe.h" + +#include "event-util.h" #include "process-util.h" +#include "util.h" static void pppoe_handler(sd_pppoe *ppp, int event, void *userdata) { static int pppoe_state = -1; @@ -41,12 +42,12 @@ static void pppoe_handler(sd_pppoe *ppp, int event, void *userdata) { assert_se(e); switch (event) { - case PPPOE_EVENT_RUNNING: + case SD_PPPOE_EVENT_RUNNING: assert_se(pppoe_state == -1); log_info("running"); break; - case PPPOE_EVENT_STOPPED: - assert_se(pppoe_state == PPPOE_EVENT_RUNNING); + case SD_PPPOE_EVENT_STOPPED: + assert_se(pppoe_state == SD_PPPOE_EVENT_RUNNING); log_info("stopped"); assert_se(sd_event_exit(e, 0) >= 0); break; |