sd-id128/id128-util.c
sd-id128/id128-util.h
sd-id128/sd-id128.c
+ sd-netlink/generic-netlink.c
sd-netlink/local-addresses.c
sd-netlink/local-addresses.h
sd-netlink/netlink-internal.h
--- /dev/null
+#include <linux/genetlink.h>
+
+#include "sd-netlink.h"
+#include "netlink-internal.h"
+#include "alloc-util.h"
+
+typedef struct {
+ const char* name;
+ uint8_t version;
+} genl_family;
+
+static const genl_family genl_families[] = {
+ [SD_GENL_ID_CTRL] = { .name = "", .version = 1 },
+};
+
+int sd_genl_socket_open(sd_netlink **ret) {
+ return netlink_open_family(ret, NETLINK_GENERIC);
+}
+static int lookup_id(sd_netlink *nl, sd_genl_family family, uint16_t *id);
+
+static int genl_message_new(sd_netlink *nl, sd_genl_family family, uint16_t nlmsg_type, uint8_t cmd, sd_netlink_message **ret) {
+ int r;
+ struct genlmsghdr *genl;
+ const NLType *genl_cmd_type, *nl_type;
+ const NLTypeSystem *type_system;
+ size_t size;
+ _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
+
+ assert_return(nl->protocol == NETLINK_GENERIC, -EINVAL);
+
+ r = type_system_get_type(&genl_family_type_system_root, &genl_cmd_type, family);
+ if (r < 0)
+ return r;
+
+ r = message_new_empty(nl, &m);
+ if (r < 0)
+ return r;
+
+ size = NLMSG_SPACE(sizeof(struct genlmsghdr));
+ m->hdr = malloc0(size);
+ if (!m->hdr)
+ return -ENOMEM;
+
+ m->hdr->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
+
+ type_get_type_system(genl_cmd_type, &type_system);
+
+ r = type_system_get_type(type_system, &nl_type, cmd);
+ if (r < 0)
+ return r;
+
+ m->hdr->nlmsg_len = size;
+ m->hdr->nlmsg_type = nlmsg_type;
+
+ type_get_type_system(nl_type, &m->containers[0].type_system);
+ genl = NLMSG_DATA(m->hdr);
+ genl->cmd = cmd;
+ genl->version = genl_families[family].version;
+
+ *ret = m;
+ m = NULL;
+
+ return 0;
+}
+
+int sd_genl_message_new(sd_netlink *nl, sd_genl_family family, uint8_t cmd, sd_netlink_message **ret) {
+ int r;
+ uint16_t id = GENL_ID_CTRL;
+
+ if (family != SD_GENL_ID_CTRL) {
+ r = lookup_id(nl, family, &id);
+ if (r < 0)
+ return r;
+ }
+
+ return genl_message_new(nl, family, id, cmd, ret);
+}
+
+static int lookup_id(sd_netlink *nl, sd_genl_family family, uint16_t *id) {
+ int r;
+ _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL, *reply = NULL;
+
+ r = sd_genl_message_new(nl, SD_GENL_ID_CTRL, CTRL_CMD_GETFAMILY, &req);
+ if (r < 0)
+ return r;
+
+ r = sd_netlink_message_append_string(req, CTRL_ATTR_FAMILY_NAME, genl_families[family].name);
+ if (r < 0)
+ return r;
+
+ r = sd_netlink_call(nl, req, 0, &reply);
+ if (r < 0)
+ return r;
+
+ return sd_netlink_message_read_u16(reply, CTRL_ATTR_FAMILY_ID, id);
+}
struct sockaddr_nl nl;
} sockaddr;
+ int protocol;
+
Hashmap *broadcast_group_refs;
bool broadcast_group_dont_leave:1; /* until we can rely on 4.2 */
sd_netlink *rtnl;
+ int protocol;
+
struct nlmsghdr *hdr;
struct netlink_container containers[RTNL_CONTAINER_DEPTH];
unsigned n_containers; /* number of containers */
int message_new(sd_netlink *rtnl, sd_netlink_message **ret, uint16_t type);
int message_new_empty(sd_netlink *rtnl, sd_netlink_message **ret);
+int netlink_open_family(sd_netlink **ret, int family);
+
int socket_open(int family);
int socket_bind(sd_netlink *nl);
int socket_broadcast_group_ref(sd_netlink *nl, unsigned group);
return -ENOMEM;
m->n_ref = REFCNT_INIT;
-
+ m->protocol = rtnl->protocol;
m->sealed = false;
*ret = m;
int message_new(sd_netlink *rtnl, sd_netlink_message **ret, uint16_t type) {
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
const NLType *nl_type;
+ const NLTypeSystem *type_system_root;
size_t size;
int r;
- r = type_system_get_type(&type_system_root, &nl_type, type);
+ assert_return(rtnl, -EINVAL);
+
+ type_system_root = type_system_get_root(rtnl->protocol);
+
+ r = type_system_get_type(type_system_root, &nl_type, type);
if (r < 0)
return r;
/* get the new message size (with padding at the end) */
message_length = offset + RTA_ALIGN(rta_length);
+ /* buffer should be smaller than both one page or 8K to be accepted by the kernel */
+ if (message_length > MIN(page_size(), 8192UL))
+ return -ENOBUFS;
+
/* realloc to fit the new attribute */
new_hdr = realloc(m->hdr, message_length);
if (!new_hdr)
if (r < 0)
return r;
- /* do we evere need non-null size */
+ /* do we ever need non-null size */
r = add_rtattr(m, type | NLA_F_NESTED, NULL, 0);
if (r < 0)
return r;
return 0;
}
-
int sd_netlink_message_close_container(sd_netlink_message *m) {
assert_return(m, -EINVAL);
assert_return(!m->sealed, -EPERM);
assert_return(m->n_containers > 0, -EINVAL);
m->containers[m->n_containers].type_system = NULL;
+ m->containers[m->n_containers].offset = 0;
+ m->n_containers--;
+
+ return 0;
+}
+
+int sd_netlink_message_open_array(sd_netlink_message *m, uint16_t type) {
+ int r;
+
+ assert_return(m, -EINVAL);
+ assert_return(!m->sealed, -EPERM);
+ assert_return(m->n_containers > 0, -EINVAL);
+
+ r = add_rtattr(m, type | NLA_F_NESTED, NULL, 0);
+ if (r < 0)
+ return r;
+
+ m->containers[m->n_containers].offset = r;
+ m->n_containers++;
+ m->containers[m->n_containers].type_system = m->containers[m->n_containers - 1].type_system;
+
+ return 0;
+}
+
+int sd_netlink_message_cancel_array(sd_netlink_message *m) {
+ unsigned i;
+ uint32_t rta_len;
+
+ assert_return(m, -EINVAL);
+ assert_return(!m->sealed, -EPERM);
+ assert_return(m->n_containers > 1, -EINVAL);
+
+ rta_len = GET_CONTAINER(m, (m->n_containers - 1))->rta_len;
+
+ for (i = 0; i < m->n_containers; i++)
+ GET_CONTAINER(m, i)->rta_len -= rta_len;
+
+ m->hdr->nlmsg_len -= rta_len;
+
m->n_containers--;
+ m->containers[m->n_containers].type_system = NULL;
return 0;
}
int sd_netlink_message_rewind(sd_netlink_message *m) {
const NLType *nl_type;
+ const NLTypeSystem *type_system_root;
uint16_t type;
size_t size;
unsigned i;
if (!m->sealed)
rtnl_message_seal(m);
+ type_system_root = type_system_get_root(m->protocol);
+
for (i = 1; i <= m->n_containers; i++)
m->containers[i].attributes = mfree(m->containers[i].attributes);
assert(m->hdr);
- r = type_system_get_type(&type_system_root, &nl_type, m->hdr->nlmsg_type);
+ r = type_system_get_type(type_system_root, &nl_type, m->hdr->nlmsg_type);
if (r < 0)
return r;
size_t len;
int r;
unsigned i = 0;
+ const NLTypeSystem *type_system_root;
assert(rtnl);
assert(rtnl->rbuffer);
assert(rtnl->rbuffer_allocated >= sizeof(struct nlmsghdr));
+ type_system_root = type_system_get_root(rtnl->protocol);
+
/* read nothing, just get the pending message size */
r = socket_recv_message(rtnl->fd, &iov, NULL, true);
if (r <= 0)
}
/* check that we support this message type */
- r = type_system_get_type(&type_system_root, &nl_type, new_msg->nlmsg_type);
+ r = type_system_get_type(type_system_root, &nl_type, new_msg->nlmsg_type);
+
if (r < 0) {
if (r == -EOPNOTSUPP)
log_debug("sd-netlink: ignored message with unknown type: %i",
#include <linux/if_link.h>
#include <linux/if_tunnel.h>
#include <linux/fib_rules.h>
+#include <linux/genetlink.h>
#if HAVE_VXCAN_INFO_PEER
#include <linux/can/vxcan.h>
#include "netlink-types.h"
#include "string-table.h"
#include "util.h"
+#include "sd-netlink.h"
/* Maximum ARP IP target defined in kernel */
#define BOND_MAX_ARP_TARGETS 16
[RTM_GETRULE] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_routing_policy_rule_type_system, .size = sizeof(struct rtmsg) },
};
-const NLTypeSystem type_system_root = {
+const NLTypeSystem rtnl_type_system_root = {
.count = ELEMENTSOF(rtnl_types),
.types = rtnl_types,
};
+
+static const NLType genl_get_family_types[] = {
+ [CTRL_ATTR_FAMILY_NAME] = { .type = NETLINK_TYPE_STRING },
+ [CTRL_ATTR_FAMILY_ID] = { .type = NETLINK_TYPE_U16 },
+};
+
+static const NLTypeSystem genl_get_family_type_system = {
+ .count = ELEMENTSOF(genl_get_family_types),
+ .types = genl_get_family_types,
+};
+
+static const NLType genl_ctrl_id_ctrl_cmds[] = {
+ [CTRL_CMD_GETFAMILY] = { .type = NETLINK_TYPE_NESTED, .type_system = &genl_get_family_type_system },
+};
+
+static const NLTypeSystem genl_ctrl_id_ctrl_type_system = {
+ .count = ELEMENTSOF(genl_ctrl_id_ctrl_cmds),
+ .types = genl_ctrl_id_ctrl_cmds,
+};
+
+static const NLType genl_families[] = {
+ [SD_GENL_ID_CTRL] = { .type = NETLINK_TYPE_NESTED, .type_system = &genl_ctrl_id_ctrl_type_system },
+};
+
+const NLTypeSystem genl_family_type_system_root = {
+ .count = ELEMENTSOF(genl_families),
+ .types = genl_families,
+};
+
+static const NLType genl_types[] = {
+ [GENL_ID_CTRL] = { .type = NETLINK_TYPE_NESTED, .type_system = &genl_get_family_type_system, .size = sizeof(struct genlmsghdr) },
+};
+
+const NLTypeSystem genl_type_system_root = {
+ .count = ELEMENTSOF(genl_types),
+ .types = genl_types,
+};
+
uint16_t type_get_type(const NLType *type) {
assert(type);
return type->type;
return type_system->count;
}
+const NLTypeSystem *type_system_get_root(int protocol) {
+ switch (protocol) {
+ case NETLINK_GENERIC:
+ return &genl_type_system_root;
+ default: /* NETLINK_ROUTE: */
+ return &rtnl_type_system_root;
+ }
+}
+
int type_system_get_type(const NLTypeSystem *type_system, const NLType **ret, uint16_t type) {
const NLType *nl_type;
const NLTypeSystem *type_systems;
};
-extern const NLTypeSystem type_system_root;
+extern const NLTypeSystem rtnl_type_system_root;
+extern const NLTypeSystem genl_type_system_root;
+extern const NLTypeSystem genl_family_type_system_root;
uint16_t type_get_type(const NLType *type);
size_t type_get_size(const NLType *type);
void type_get_type_system(const NLType *type, const NLTypeSystem **ret);
void type_get_type_system_union(const NLType *type, const NLTypeSystemUnion **ret);
+const NLTypeSystem* type_system_get_root(int protocol);
uint16_t type_system_get_count(const NLTypeSystem *type_system);
int type_system_get_type(const NLTypeSystem *type_system, const NLType **ret, uint16_t type);
int type_system_get_type_system(const NLTypeSystem *type_system, const NLTypeSystem **ret, uint16_t type);
return 0;
}
-int rtnl_message_new_synthetic_error(int error, uint32_t serial, sd_netlink_message **ret) {
+int rtnl_message_new_synthetic_error(sd_netlink *rtnl, int error, uint32_t serial, sd_netlink_message **ret) {
struct nlmsgerr *err;
int r;
assert(error <= 0);
- r = message_new(NULL, ret, NLMSG_ERROR);
+ r = message_new(rtnl, ret, NLMSG_ERROR);
if (r < 0)
return r;
#include "util.h"
-int rtnl_message_new_synthetic_error(int error, uint32_t serial, sd_netlink_message **ret);
+int rtnl_message_new_synthetic_error(sd_netlink *rtnl, int error, uint32_t serial, sd_netlink_message **ret);
uint32_t rtnl_message_get_serial(sd_netlink_message *m);
void rtnl_message_seal(sd_netlink_message *m);
rtnl->fd = -1;
rtnl->sockaddr.nl.nl_family = AF_NETLINK;
rtnl->original_pid = getpid_cached();
+ rtnl->protocol = -1;
LIST_HEAD_INIT(rtnl->match_callbacks);
int sd_netlink_open_fd(sd_netlink **ret, int fd) {
_cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL;
int r;
+ int protocol;
+ socklen_t l;
assert_return(ret, -EINVAL);
assert_return(fd >= 0, -EBADF);
if (r < 0)
return r;
+ l = sizeof(protocol);
+ r = getsockopt(fd, SOL_SOCKET, SO_PROTOCOL, &protocol, &l);
+ if (r < 0)
+ return r;
+
rtnl->fd = fd;
+ rtnl->protocol = protocol;
r = socket_bind(rtnl);
if (r < 0) {
rtnl->fd = -1; /* on failure, the caller remains owner of the fd, hence don't close it here */
+ rtnl->protocol = -1;
return r;
}
return 0;
}
-int sd_netlink_open(sd_netlink **ret) {
+int netlink_open_family(sd_netlink **ret, int family) {
_cleanup_close_ int fd = -1;
int r;
- fd = socket_open(NETLINK_ROUTE);
+ fd = socket_open(family);
if (fd < 0)
return fd;
return 0;
}
+int sd_netlink_open(sd_netlink **ret) {
+ return netlink_open_family(ret, NETLINK_ROUTE);
+}
+
int sd_netlink_inc_rcvbuf(sd_netlink *rtnl, size_t size) {
assert_return(rtnl, -EINVAL);
assert_return(!rtnl_pid_changed(rtnl), -ECHILD);
if (c->timeout > n)
return 0;
- r = rtnl_message_new_synthetic_error(-ETIMEDOUT, c->serial, &m);
+ r = rtnl_message_new_synthetic_error(rtnl, -ETIMEDOUT, c->serial, &m);
if (r < 0)
return r;
}
-static void test_route(void) {
+static void test_route(sd_netlink *rtnl) {
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req;
struct in_addr addr, addr_data;
uint32_t index = 2, u32_data;
int r;
- r = sd_rtnl_message_new_route(NULL, &req, RTM_NEWROUTE, AF_INET, RTPROT_STATIC);
+ r = sd_rtnl_message_new_route(rtnl, &req, RTM_NEWROUTE, AF_INET, RTPROT_STATIC);
if (r < 0) {
log_error_errno(r, "Could not create RTM_NEWROUTE message: %m");
return;
assert_se((rtnl = sd_netlink_unref(rtnl)) == NULL);
}
-static void test_container(void) {
+static void test_container(sd_netlink *rtnl) {
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
uint16_t u16_data;
uint32_t u32_data;
const char *string_data;
- assert_se(sd_rtnl_message_new_link(NULL, &m, RTM_NEWLINK, 0) >= 0);
+ assert_se(sd_rtnl_message_new_link(rtnl, &m, RTM_NEWLINK, 0) >= 0);
assert_se(sd_netlink_message_open_container(m, IFLA_LINKINFO) >= 0);
assert_se(sd_netlink_message_open_container_union(m, IFLA_INFO_DATA, "vlan") >= 0);
}
}
-static void test_message(void) {
+static void test_message(sd_netlink *rtnl) {
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
- assert_se(rtnl_message_new_synthetic_error(-ETIMEDOUT, 1, &m) >= 0);
+ assert_se(rtnl_message_new_synthetic_error(rtnl, -ETIMEDOUT, 1, &m) >= 0);
assert_se(sd_netlink_message_get_errno(m) == -ETIMEDOUT);
}
int if_loopback;
uint16_t type;
- test_message();
-
test_match();
test_multiple();
- test_route();
-
- test_container();
-
assert_se(sd_netlink_open(&rtnl) >= 0);
assert_se(rtnl);
+ test_route(rtnl);
+
+ test_message(rtnl);
+
+ test_container(rtnl);
+
if_loopback = (int) if_nametoindex("lo");
assert_se(if_loopback > 0);
if (!netdev)
return;
- rtnl_message_new_synthetic_error(-ENODEV, 0, &m);
+ rtnl_message_new_synthetic_error(netdev->manager->rtnl, -ENODEV, 0, &m);
while ((callback = netdev->callbacks)) {
if (m) {
} else if (IN_SET(netdev->state, NETDEV_STATE_LINGER, NETDEV_STATE_FAILED)) {
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
- r = rtnl_message_new_synthetic_error(-ENODEV, 0, &m);
+ r = rtnl_message_new_synthetic_error(netdev->manager->rtnl, -ENODEV, 0, &m);
if (r >= 0)
callback(netdev->manager->rtnl, m, link);
} else {
return rtnl_fd;
}
+static int manager_connect_genl(Manager *m) {
+ int r;
+
+ assert(m);
+
+ r = sd_genl_socket_open(&m->genl);
+ if (r < 0)
+ return r;
+
+ r = sd_netlink_inc_rcvbuf(m->genl, RCVBUF_SIZE);
+ if (r < 0)
+ return r;
+
+ r = sd_netlink_attach_event(m->genl, m->event, 0);
+ if (r < 0)
+ return r;
+
+ return 0;
+}
+
static int manager_connect_rtnl(Manager *m) {
int fd, r;
if (r < 0)
return r;
+ r = manager_connect_genl(m);
+ if (r < 0)
+ return r;
+
r = manager_connect_udev(m);
if (r < 0)
return r;
LIST_HEAD_INIT(m->networks);
+ r = sd_resolve_default(&m->resolve);
+ if (r < 0)
+ return r;
+
+ r = sd_resolve_attach_event(m->resolve, m->event, 0);
+ if (r < 0)
+ return r;
+
r = setup_default_address_pool(m);
if (r < 0)
return r;
sd_netlink_unref(m->rtnl);
sd_event_unref(m->event);
+ sd_resolve_unref(m->resolve);
+
sd_event_source_unref(m->udev_event_source);
udev_monitor_unref(m->udev_monitor);
udev_unref(m->udev);
#include "sd-bus.h"
#include "sd-event.h"
#include "sd-netlink.h"
+#include "sd-resolve.h"
#include "udev.h"
#include "dhcp-identifier.h"
struct Manager {
sd_netlink *rtnl;
+ /* lazy initialized */
+ sd_netlink *genl;
sd_event *event;
+ sd_resolve *resolve;
sd_event_source *bus_retry_event_source;
sd_bus *bus;
sd_bus_slot *prepare_for_sleep_slot;
_SD_BEGIN_DECLARATIONS;
typedef struct sd_netlink sd_netlink;
+typedef struct sd_genl_socket sd_genl_socket;
typedef struct sd_netlink_message sd_netlink_message;
+typedef enum {SD_GENL_ID_CTRL} sd_genl_family;
/* callback */
int sd_netlink_message_enter_container(sd_netlink_message *m, unsigned short type);
int sd_netlink_message_exit_container(sd_netlink_message *m);
+int sd_netlink_message_open_array(sd_netlink_message *m, uint16_t type);
+int sd_netlink_message_cancel_array(sd_netlink_message *m);
+
int sd_netlink_message_rewind(sd_netlink_message *m);
sd_netlink_message *sd_netlink_message_next(sd_netlink_message *m);
_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_netlink, sd_netlink_unref);
_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_netlink_message, sd_netlink_message_unref);
+/* genl */
+int sd_genl_socket_open(sd_netlink **nl);
+int sd_genl_message_new(sd_netlink *nl, sd_genl_family family, uint8_t cmd, sd_netlink_message **m);
+
_SD_END_DECLARATIONS;
#endif