Compare commits

...

10 Commits

Author SHA1 Message Date
Zbigniew Jędrzejewski-Szmek 0ce8092109
Merge pull request #15466 from elmarco/dhcp-leases
networkctl: print DHCP leases
2020-06-05 08:47:35 +02:00
Marc-André Lureau d9ce1c2493 networkctl: print DHCP leases 2020-06-04 11:16:15 +02:00
Marc-André Lureau 090c923b4d networkctl: factor out link_get_property()
The next patch is going to reuse it.
2020-06-04 11:16:15 +02:00
Marc-André Lureau 7be3261222 sd-dhcp-client: add sd_dhcp_client_id_to_string()
Add a human-friendly pretty-printer for client ID.
2020-06-04 11:16:15 +02:00
Marc-André Lureau aaec2d7b9b macro.h: add sizeof_field() helper
Similar to kernel FIELD_SIZE()
2020-06-04 11:16:15 +02:00
Marc-André Lureau 00ebe01156 sd-dhcp-client: make struct sd_dhcp_client_id reusable 2020-06-04 11:16:15 +02:00
Marc-André Lureau cf72a78660 networkd: expose DHCPServer interface on links 2020-06-04 11:16:15 +02:00
Marc-André Lureau 38ee31a6e4 networkd: start a DBus interface for the DHCP server
Add a "org.freedesktop.network1.DHCPServer" DBus interface that will be
added on a link path where a DHCP server is provided.

Currently, it only exposes a "Leases" property, although there are plans
to expand it further. The property is updated thanks to the
dhcp_server_callback().
2020-06-04 11:16:15 +02:00
Marc-André Lureau 36d35f2259 sd-dhcp-server: notify callback on lease changed 2020-06-04 11:16:15 +02:00
Marc-André Lureau 5b03043a75 sd-dhcp-server: add sd_dhcp_server_set_callback() 2020-06-04 11:04:06 +02:00
12 changed files with 362 additions and 34 deletions

View File

@ -448,6 +448,8 @@ static inline int __coverity_check_and_return__(int condition) {
#define char_array_0(x) x[sizeof(x)-1] = 0;
#define sizeof_field(struct_type, member) sizeof(((struct_type *) 0)->member)
/* Returns the number of chars needed to format variables of the
* specified type as a decimal string. Adds in extra space for a
* negative '-' prefix (hence works correctly on signed

View File

@ -68,6 +68,9 @@ struct sd_dhcp_server {
DHCPLease invalid_lease;
uint32_t max_lease_time, default_lease_time;
sd_dhcp_server_callback_t callback;
void *callback_userdata;
};
typedef struct DHCPRequest {

View File

@ -27,6 +27,7 @@
#include "random-util.h"
#include "string-util.h"
#include "strv.h"
#include "utf8.h"
#include "web-util.h"
#define MAX_CLIENT_ID_LEN (sizeof(uint32_t) + MAX_DUID_LEN) /* Arbitrary limit */
@ -35,6 +36,32 @@
#define RESTART_AFTER_NAK_MIN_USEC (1 * USEC_PER_SEC)
#define RESTART_AFTER_NAK_MAX_USEC (30 * USEC_PER_MINUTE)
typedef struct sd_dhcp_client_id {
uint8_t type;
union {
struct {
/* 0: Generic (non-LL) (RFC 2132) */
uint8_t data[MAX_CLIENT_ID_LEN];
} _packed_ gen;
struct {
/* 1: Ethernet Link-Layer (RFC 2132) */
uint8_t haddr[ETH_ALEN];
} _packed_ eth;
struct {
/* 2 - 254: ARP/Link-Layer (RFC 2132) */
uint8_t haddr[0];
} _packed_ ll;
struct {
/* 255: Node-specific (RFC 4361) */
be32_t iaid;
struct duid duid;
} _packed_ ns;
struct {
uint8_t data[MAX_CLIENT_ID_LEN];
} _packed_ raw;
};
} _packed_ sd_dhcp_client_id;
struct sd_dhcp_client {
unsigned n_ref;
@ -56,31 +83,7 @@ struct sd_dhcp_client {
uint8_t mac_addr[MAX_MAC_ADDR_LEN];
size_t mac_addr_len;
uint16_t arp_type;
struct {
uint8_t type;
union {
struct {
/* 0: Generic (non-LL) (RFC 2132) */
uint8_t data[MAX_CLIENT_ID_LEN];
} _packed_ gen;
struct {
/* 1: Ethernet Link-Layer (RFC 2132) */
uint8_t haddr[ETH_ALEN];
} _packed_ eth;
struct {
/* 2 - 254: ARP/Link-Layer (RFC 2132) */
uint8_t haddr[0];
} _packed_ ll;
struct {
/* 255: Node-specific (RFC 4361) */
be32_t iaid;
struct duid duid;
} _packed_ ns;
struct {
uint8_t data[MAX_CLIENT_ID_LEN];
} _packed_ raw;
};
} _packed_ client_id;
sd_dhcp_client_id client_id;
size_t client_id_len;
char *hostname;
char *vendor_class_identifier;
@ -151,6 +154,60 @@ static int client_receive_message_udp(
void *userdata);
static void client_stop(sd_dhcp_client *client, int error);
int sd_dhcp_client_id_to_string(const void *data, size_t len, char **ret) {
const sd_dhcp_client_id *client_id = data;
_cleanup_free_ char *t = NULL;
int r = 0;
assert_return(data, -EINVAL);
assert_return(len >= 1, -EINVAL);
assert_return(ret, -EINVAL);
len -= 1;
if (len > MAX_CLIENT_ID_LEN)
return -EINVAL;
switch (client_id->type) {
case 0:
if (utf8_is_printable((char *) client_id->gen.data, len))
r = asprintf(&t, "%.*s", (int) len, client_id->gen.data);
else
r = asprintf(&t, "DATA");
break;
case 1:
if (len != sizeof_field(sd_dhcp_client_id, eth))
return -EINVAL;
r = asprintf(&t, "%x:%x:%x:%x:%x:%x",
client_id->eth.haddr[0],
client_id->eth.haddr[1],
client_id->eth.haddr[2],
client_id->eth.haddr[3],
client_id->eth.haddr[4],
client_id->eth.haddr[5]);
break;
case 2 ... 254:
r = asprintf(&t, "ARP/LL");
break;
case 255:
if (len < 6)
return -EINVAL;
uint32_t iaid = be32toh(client_id->ns.iaid);
uint16_t duid_type = be16toh(client_id->ns.duid.type);
if (dhcp_validate_duid_len(duid_type, len - 6, true) < 0)
return -EINVAL;
r = asprintf(&t, "IAID:0x%x/DUID", iaid);
break;
}
if (r < 0)
return -ENOMEM;
*ret = TAKE_PTR(t);
return 0;
}
int sd_dhcp_client_set_callback(
sd_dhcp_client *client,
sd_dhcp_client_callback_t cb,

View File

@ -98,6 +98,9 @@ int sd_dhcp_server_configure_pool(
/* Drop any leases associated with the old address range */
hashmap_clear(server->leases_by_client_id);
if (server->callback)
server->callback(server, SD_DHCP_SERVER_EVENT_LEASE_CHANGED, server->callback_userdata);
}
return 0;
@ -899,6 +902,9 @@ int dhcp_server_handle_message(sd_dhcp_server *server, DHCPMessage *message,
hashmap_put(server->leases_by_client_id,
&lease->client_id, lease);
if (server->callback)
server->callback(server, SD_DHCP_SERVER_EVENT_LEASE_CHANGED, server->callback_userdata);
return DHCP_ACK;
}
@ -935,6 +941,9 @@ int dhcp_server_handle_message(sd_dhcp_server *server, DHCPMessage *message,
server->bound_leases[pool_offset] = NULL;
hashmap_remove(server->leases_by_client_id, existing_lease);
dhcp_lease_free(existing_lease);
if (server->callback)
server->callback(server, SD_DHCP_SERVER_EVENT_LEASE_CHANGED, server->callback_userdata);
}
return 0;
@ -1202,3 +1211,12 @@ int sd_dhcp_server_add_vendor_option(sd_dhcp_server *server, sd_dhcp_option *v)
return 1;
}
int sd_dhcp_server_set_callback(sd_dhcp_server *server, sd_dhcp_server_callback_t cb, void *userdata) {
assert_return(server, -EINVAL);
server->callback = cb;
server->callback_userdata = userdata;
return 0;
}

View File

@ -61,6 +61,8 @@ sources = files('''
networkd-conf.h
networkd-dhcp-common.c
networkd-dhcp-common.h
networkd-dhcp-server-bus.c
networkd-dhcp-server-bus.h
networkd-dhcp-server.c
networkd-dhcp-server.h
networkd-dhcp4.c

View File

@ -12,6 +12,7 @@
#include "sd-bus.h"
#include "sd-device.h"
#include "sd-dhcp-client.h"
#include "sd-hwdb.h"
#include "sd-lldp.h"
#include "sd-netlink.h"
@ -471,9 +472,13 @@ static int decode_link(sd_netlink_message *m, LinkInfo *info, char **patterns, b
return 1;
}
static int acquire_link_bitrates(sd_bus *bus, LinkInfo *link) {
_cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
static int link_get_property(
sd_bus *bus,
const LinkInfo *link,
sd_bus_error *error,
sd_bus_message **reply,
const char *iface,
const char *propname) {
_cleanup_free_ char *path = NULL, *ifindex_str = NULL;
int r;
@ -484,17 +489,25 @@ static int acquire_link_bitrates(sd_bus *bus, LinkInfo *link) {
if (r < 0)
return r;
r = sd_bus_call_method(
return sd_bus_call_method(
bus,
"org.freedesktop.network1",
path,
"org.freedesktop.DBus.Properties",
"Get",
&error,
&reply,
error,
reply,
"ss",
"org.freedesktop.network1.Link",
"BitRates");
iface,
propname);
}
static int acquire_link_bitrates(sd_bus *bus, LinkInfo *link) {
_cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
int r;
r = link_get_property(bus, link, &error, &reply, "org.freedesktop.network1.Link", "BitRates");
if (r < 0) {
bool quiet = sd_bus_error_has_name(&error, SD_BUS_ERROR_UNKNOWN_PROPERTY) ||
sd_bus_error_has_name(&error, BUS_ERROR_SPEED_METER_INACTIVE);
@ -1150,6 +1163,96 @@ static int dump_lldp_neighbors(Table *table, const char *prefix, int ifindex) {
return dump_list(table, prefix, buf);
}
static int dump_dhcp_leases(Table *table, const char *prefix, sd_bus *bus, const LinkInfo *link) {
_cleanup_strv_free_ char **buf = NULL;
_cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
int r;
r = link_get_property(bus, link, &error, &reply, "org.freedesktop.network1.DHCPServer", "Leases");
if (r < 0) {
bool quiet = sd_bus_error_has_name(&error, SD_BUS_ERROR_UNKNOWN_PROPERTY);
log_full_errno(quiet ? LOG_DEBUG : LOG_WARNING,
r, "Failed to query link DHCP leases: %s", bus_error_message(&error, r));
return 0;
}
r = sd_bus_message_enter_container(reply, 'v', "a(uayayayayt)");
if (r < 0)
return bus_log_parse_error(r);
r = sd_bus_message_enter_container(reply, 'a', "(uayayayayt)");
if (r < 0)
return bus_log_parse_error(r);
while ((r = sd_bus_message_enter_container(reply, 'r', "uayayayayt")) > 0) {
_cleanup_free_ char *id = NULL, *ip = NULL;
const void *client_id, *addr, *gtw, *hwaddr;
size_t client_id_sz, sz;
uint64_t expiration;
uint32_t family;
r = sd_bus_message_read(reply, "u", &family);
if (r < 0)
return bus_log_parse_error(r);
r = sd_bus_message_read_array(reply, 'y', &client_id, &client_id_sz);
if (r < 0)
return bus_log_parse_error(r);
r = sd_bus_message_read_array(reply, 'y', &addr, &sz);
if (r < 0 || sz != 4)
return bus_log_parse_error(r);
r = sd_bus_message_read_array(reply, 'y', &gtw, &sz);
if (r < 0 || sz != 4)
return bus_log_parse_error(r);
r = sd_bus_message_read_array(reply, 'y', &hwaddr, &sz);
if (r < 0)
return bus_log_parse_error(r);
r = sd_bus_message_read_basic(reply, 't', &expiration);
if (r < 0)
return bus_log_parse_error(r);
r = sd_dhcp_client_id_to_string(client_id, client_id_sz, &id);
if (r < 0)
return bus_log_parse_error(r);
r = in_addr_to_string(family, addr, &ip);
if (r < 0)
return bus_log_parse_error(r);
r = strv_extendf(&buf, "%s (to %s)", ip, id);
if (r < 0)
return log_oom();
r = sd_bus_message_exit_container(reply);
if (r < 0)
return bus_log_parse_error(r);
}
if (r < 0)
return bus_log_parse_error(r);
r = sd_bus_message_exit_container(reply);
if (r < 0)
return bus_log_parse_error(r);
r = sd_bus_message_exit_container(reply);
if (r < 0)
return bus_log_parse_error(r);
if (strv_isempty(buf)) {
r = strv_extendf(&buf, "none");
if (r < 0)
return log_oom();
}
return dump_list(table, prefix, buf);
}
static int dump_ifindexes(Table *table, const char *prefix, const int *ifindexes) {
unsigned c;
int r;
@ -1270,6 +1373,7 @@ static int show_logs(const LinkInfo *info) {
}
static int link_status_one(
sd_bus *bus,
sd_netlink *rtnl,
sd_hwdb *hwdb,
const LinkInfo *info) {
@ -1973,6 +2077,10 @@ static int link_status_one(
if (r < 0)
return r;
r = dump_dhcp_leases(table, "Offered DHCP leases:", bus, info);
if (r < 0)
return r;
r = dump_statistics(table, info);
if (r < 0)
return r;
@ -2090,7 +2198,7 @@ static int link_status(int argc, char *argv[], void *userdata) {
if (i > 0)
fputc('\n', stdout);
link_status_one(rtnl, hwdb, links + i);
link_status_one(bus, rtnl, hwdb, links + i);
}
return 0;

View File

@ -0,0 +1,111 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#include "sd-dhcp-server.h"
#include "alloc-util.h"
#include "bus-common-errors.h"
#include "bus-util.h"
#include "dhcp-server-internal.h"
#include "networkd-dhcp-server-bus.h"
#include "networkd-link-bus.h"
#include "networkd-manager.h"
#include "strv.h"
static int property_get_leases(
sd_bus *bus,
const char *path,
const char *interface,
const char *property,
sd_bus_message *reply,
void *userdata,
sd_bus_error *error) {
Link *l = userdata;
sd_dhcp_server *s;
DHCPLease *lease;
Iterator i;
int r;
assert(reply);
assert(l);
s = l->dhcp_server;
if (!s)
return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Link %s has no DHCP server.", l->ifname);
r = sd_bus_message_open_container(reply, 'a', "(uayayayayt)");
if (r < 0)
return r;
HASHMAP_FOREACH(lease, s->leases_by_client_id, i) {
r = sd_bus_message_open_container(reply, 'r', "uayayayayt");
if (r < 0)
return r;
r = sd_bus_message_append(reply, "u", (uint32_t)AF_INET);
if (r < 0)
return r;
r = sd_bus_message_append_array(reply, 'y', lease->client_id.data, lease->client_id.length);
if (r < 0)
return r;
r = sd_bus_message_append_array(reply, 'y', &lease->address, sizeof(lease->address));
if (r < 0)
return r;
r = sd_bus_message_append_array(reply, 'y', &lease->gateway, sizeof(lease->gateway));
if (r < 0)
return r;
r = sd_bus_message_append_array(reply, 'y', &lease->chaddr, sizeof(lease->chaddr));
if (r < 0)
return r;
r = sd_bus_message_append_basic(reply, 't', &lease->expiration);
if (r < 0)
return r;
r = sd_bus_message_close_container(reply);
if (r < 0)
return r;
}
return sd_bus_message_close_container(reply);
}
static int dhcp_server_emit_changed(Link *link, const char *property, ...) {
_cleanup_free_ char *path = NULL;
char **l;
assert(link);
path = link_bus_path(link);
if (!path)
return log_oom();
l = strv_from_stdarg_alloca(property);
return sd_bus_emit_properties_changed_strv(
link->manager->bus,
path,
"org.freedesktop.network1.DHCPServer",
l);
}
void dhcp_server_callback(sd_dhcp_server *s, uint64_t event, void *data) {
Link *l = data;
assert(l);
if (event & SD_DHCP_SERVER_EVENT_LEASE_CHANGED)
(void) dhcp_server_emit_changed(l, "Leases", NULL);
}
const sd_bus_vtable dhcp_server_vtable[] = {
SD_BUS_VTABLE_START(0),
SD_BUS_PROPERTY("Leases", "a(uayayayayt)", property_get_leases, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
SD_BUS_VTABLE_END
};

View File

@ -0,0 +1,9 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#pragma once
#include "sd-bus.h"
#include "networkd-link.h"
extern const sd_bus_vtable dhcp_server_vtable[];
void dhcp_server_callback(sd_dhcp_server *server, uint64_t event, void *data);

View File

@ -760,6 +760,9 @@ int link_object_find(sd_bus *bus, const char *path, const char *interface, void
if (r < 0)
return 0;
if (streq(interface, "org.freedesktop.network1.DHCPServer") && !link->dhcp_server)
return 0;
*found = link;
return 1;

View File

@ -24,6 +24,7 @@
#include "local-addresses.h"
#include "netlink-util.h"
#include "network-internal.h"
#include "networkd-dhcp-server-bus.h"
#include "networkd-dhcp6.h"
#include "networkd-link-bus.h"
#include "networkd-manager-bus.h"
@ -154,6 +155,10 @@ int manager_connect_bus(Manager *m) {
if (r < 0)
return log_error_errno(r, "Failed to add link object vtable: %m");
r = sd_bus_add_fallback_vtable(m->bus, NULL, "/org/freedesktop/network1/link", "org.freedesktop.network1.DHCPServer", dhcp_server_vtable, link_object_find, m);
if (r < 0)
return log_error_errno(r, "Failed to add link object vtable: %m");
r = sd_bus_add_node_enumerator(m->bus, NULL, "/org/freedesktop/network1/link", link_node_enumerator, m);
if (r < 0)
return log_error_errno(r, "Failed to add link enumerator: %m");

View File

@ -205,6 +205,8 @@ sd_dhcp_client *sd_dhcp_client_unref(sd_dhcp_client *client);
* options when using RFC7844 Anonymity Profiles */
int sd_dhcp_client_new(sd_dhcp_client **ret, int anonymize);
int sd_dhcp_client_id_to_string(const void *data, size_t len, char **ret);
int sd_dhcp_client_attach_event(
sd_dhcp_client *client,
sd_event *event,

View File

@ -31,6 +31,10 @@ _SD_BEGIN_DECLARATIONS;
typedef struct sd_dhcp_server sd_dhcp_server;
enum {
SD_DHCP_SERVER_EVENT_LEASE_CHANGED = 1 << 0,
};
int sd_dhcp_server_new(sd_dhcp_server **ret, int ifindex);
sd_dhcp_server *sd_dhcp_server_ref(sd_dhcp_server *server);
@ -40,6 +44,10 @@ int sd_dhcp_server_attach_event(sd_dhcp_server *client, sd_event *event, int64_t
int sd_dhcp_server_detach_event(sd_dhcp_server *client);
sd_event *sd_dhcp_server_get_event(sd_dhcp_server *client);
typedef void (*sd_dhcp_server_callback_t)(sd_dhcp_server *server, uint64_t event, void *userdata);
int sd_dhcp_server_set_callback(sd_dhcp_server *server, sd_dhcp_server_callback_t cb, void *userdata);
int sd_dhcp_server_is_running(sd_dhcp_server *server);
int sd_dhcp_server_start(sd_dhcp_server *server);