sd-netlink: drop sd_genl_family_t and introduce GenericNetlinkFamily
Kernel manages each genl family by its name, e.g. "nlctrl" or WG_GENL_NAME, and its ID (used for nlmsg_type) is determined dynamically when the corresponding module is loaded. This commit makes sd-netlink follow the same way; now, sd_genl_family_t is dropped, and sd_genl_message_new() takes a genl family name. Each genl family is resolved when it is used first time, and its information is stored in GenericNetlinkFamily.
This commit is contained in:
@@ -7,91 +7,196 @@
|
||||
#include "alloc-util.h"
|
||||
#include "netlink-genl.h"
|
||||
#include "netlink-internal.h"
|
||||
#include "netlink-types.h"
|
||||
|
||||
typedef struct {
|
||||
const char* name;
|
||||
uint8_t version;
|
||||
} genl_family;
|
||||
typedef struct GenericNetlinkFamily {
|
||||
sd_netlink *genl;
|
||||
|
||||
static const genl_family genl_families[] = {
|
||||
[SD_GENL_ID_CTRL] = { .name = "", .version = 1 },
|
||||
[SD_GENL_WIREGUARD] = { .name = "wireguard", .version = 1 },
|
||||
[SD_GENL_FOU] = { .name = "fou", .version = 1 },
|
||||
[SD_GENL_L2TP] = { .name = "l2tp", .version = 1 },
|
||||
[SD_GENL_MACSEC] = { .name = "macsec", .version = 1 },
|
||||
[SD_GENL_NL80211] = { .name = "nl80211", .version = 1 },
|
||||
[SD_GENL_BATADV] = { .name = "batadv", .version = 1 },
|
||||
const NLTypeSystem *type_system;
|
||||
|
||||
uint16_t id; /* a.k.a nlmsg_type */
|
||||
char *name;
|
||||
} GenericNetlinkFamily;
|
||||
|
||||
static const GenericNetlinkFamily nlctrl_static = {
|
||||
.id = GENL_ID_CTRL,
|
||||
.name = (char*) CTRL_GENL_NAME,
|
||||
};
|
||||
|
||||
int sd_genl_socket_open(sd_netlink **ret) {
|
||||
return netlink_open_family(ret, NETLINK_GENERIC);
|
||||
static GenericNetlinkFamily *genl_family_free(GenericNetlinkFamily *f) {
|
||||
if (!f)
|
||||
return NULL;
|
||||
|
||||
if (f->genl) {
|
||||
if (f->id > 0)
|
||||
hashmap_remove(f->genl->genl_family_by_id, UINT_TO_PTR(f->id));
|
||||
if (f->name)
|
||||
hashmap_remove(f->genl->genl_family_by_name, f->name);
|
||||
}
|
||||
|
||||
free(f->name);
|
||||
|
||||
return mfree(f);
|
||||
}
|
||||
|
||||
static int genl_message_new(sd_netlink *nl, sd_genl_family_t family, uint16_t nlmsg_type, uint8_t cmd, sd_netlink_message **ret) {
|
||||
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
|
||||
const NLType *type;
|
||||
size_t size;
|
||||
DEFINE_TRIVIAL_CLEANUP_FUNC(GenericNetlinkFamily*, genl_family_free);
|
||||
|
||||
void genl_clear_family(sd_netlink *nl) {
|
||||
assert(nl);
|
||||
|
||||
nl->genl_family_by_name = hashmap_free_with_destructor(nl->genl_family_by_name, genl_family_free);
|
||||
nl->genl_family_by_id = hashmap_free_with_destructor(nl->genl_family_by_id, genl_family_free);
|
||||
}
|
||||
|
||||
static int genl_family_new(
|
||||
sd_netlink *nl,
|
||||
const char *expected_family_name,
|
||||
const NLTypeSystem *type_system,
|
||||
sd_netlink_message *message,
|
||||
const GenericNetlinkFamily **ret) {
|
||||
|
||||
_cleanup_(genl_family_freep) GenericNetlinkFamily *f = NULL;
|
||||
const char *family_name;
|
||||
int r;
|
||||
|
||||
assert(nl);
|
||||
assert(nl->protocol == NETLINK_GENERIC);
|
||||
assert(expected_family_name);
|
||||
assert(type_system);
|
||||
assert(message);
|
||||
assert(ret);
|
||||
|
||||
r = type_system_root_get_type(nl, &type, nlmsg_type);
|
||||
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)
|
||||
f = new(GenericNetlinkFamily, 1);
|
||||
if (!f)
|
||||
return -ENOMEM;
|
||||
|
||||
m->hdr->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
|
||||
m->hdr->nlmsg_len = size;
|
||||
m->hdr->nlmsg_type = nlmsg_type;
|
||||
|
||||
m->containers[0].type_system = type_get_type_system(type);
|
||||
|
||||
*(struct genlmsghdr *) NLMSG_DATA(m->hdr) = (struct genlmsghdr) {
|
||||
.cmd = cmd,
|
||||
.version = genl_families[family].version,
|
||||
*f = (GenericNetlinkFamily) {
|
||||
.type_system = type_system,
|
||||
};
|
||||
|
||||
*ret = TAKE_PTR(m);
|
||||
if (sd_netlink_message_is_error(message)) {
|
||||
int e;
|
||||
|
||||
/* Kernel does not support the genl family? To prevent from resolving the family name
|
||||
* again, let's store the family with zero id to indicate that. */
|
||||
|
||||
e = sd_netlink_message_get_errno(message);
|
||||
if (e >= 0) /* Huh? */
|
||||
e = -EOPNOTSUPP;
|
||||
|
||||
f->name = strdup(expected_family_name);
|
||||
if (!f->name)
|
||||
return e;
|
||||
|
||||
if (hashmap_ensure_put(&nl->genl_family_by_name, &string_hash_ops, f->name, f) < 0)
|
||||
return e;
|
||||
|
||||
f->genl = nl;
|
||||
TAKE_PTR(f);
|
||||
return e;
|
||||
}
|
||||
|
||||
r = sd_genl_message_get_family_name(nl, message, &family_name);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (!streq(family_name, CTRL_GENL_NAME))
|
||||
return -EINVAL;
|
||||
|
||||
r = sd_netlink_message_read_u16(message, CTRL_ATTR_FAMILY_ID, &f->id);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_netlink_message_read_string_strdup(message, CTRL_ATTR_FAMILY_NAME, &f->name);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (!streq(f->name, expected_family_name))
|
||||
return -EINVAL;
|
||||
|
||||
r = hashmap_ensure_put(&nl->genl_family_by_id, NULL, UINT_TO_PTR(f->id), f);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = hashmap_ensure_put(&nl->genl_family_by_name, &string_hash_ops, f->name, f);
|
||||
if (r < 0) {
|
||||
hashmap_remove(nl->genl_family_by_id, UINT_TO_PTR(f->id));
|
||||
return r;
|
||||
}
|
||||
|
||||
f->genl = nl;
|
||||
*ret = TAKE_PTR(f);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int lookup_nlmsg_type(sd_netlink *nl, sd_genl_family_t family, uint16_t *ret) {
|
||||
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL, *reply = NULL;
|
||||
uint16_t u;
|
||||
void *v;
|
||||
static int genl_family_get_type_system(const GenericNetlinkFamily *family, const NLTypeSystem **ret) {
|
||||
assert(family);
|
||||
assert(ret);
|
||||
|
||||
if (family->type_system) {
|
||||
*ret = family->type_system;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return genl_get_type_system_by_name(family->name, ret);
|
||||
}
|
||||
|
||||
static int genl_message_new(
|
||||
sd_netlink *nl,
|
||||
const GenericNetlinkFamily *family,
|
||||
uint8_t cmd,
|
||||
sd_netlink_message **ret) {
|
||||
|
||||
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
|
||||
const NLTypeSystem *type_system;
|
||||
int r;
|
||||
|
||||
assert(nl);
|
||||
assert(nl->protocol == NETLINK_GENERIC);
|
||||
assert(family);
|
||||
assert(ret);
|
||||
|
||||
if (family == SD_GENL_ID_CTRL) {
|
||||
*ret = GENL_ID_CTRL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
v = hashmap_get(nl->genl_family_to_nlmsg_type, INT_TO_PTR(family));
|
||||
if (v) {
|
||||
*ret = PTR_TO_UINT(v);
|
||||
return 0;
|
||||
}
|
||||
|
||||
r = genl_message_new(nl, SD_GENL_ID_CTRL, GENL_ID_CTRL, CTRL_CMD_GETFAMILY, &req);
|
||||
r = genl_family_get_type_system(family, &type_system);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_netlink_message_append_string(req, CTRL_ATTR_FAMILY_NAME, genl_families[family].name);
|
||||
r = message_new_full(nl, family->id, type_system, sizeof(struct genlmsghdr), &m);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
*(struct genlmsghdr *) NLMSG_DATA(m->hdr) = (struct genlmsghdr) {
|
||||
.cmd = cmd,
|
||||
.version = 1,
|
||||
};
|
||||
|
||||
*ret = TAKE_PTR(m);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int genl_family_get_by_name_internal(
|
||||
sd_netlink *nl,
|
||||
const GenericNetlinkFamily *ctrl,
|
||||
const char *name,
|
||||
const GenericNetlinkFamily **ret) {
|
||||
|
||||
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL, *reply = NULL;
|
||||
const NLTypeSystem *type_system;
|
||||
int r;
|
||||
|
||||
assert(nl);
|
||||
assert(nl->protocol == NETLINK_GENERIC);
|
||||
assert(ctrl);
|
||||
assert(name);
|
||||
assert(ret);
|
||||
|
||||
r = genl_get_type_system_by_name(name, &type_system);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = genl_message_new(nl, ctrl, CTRL_CMD_GETFAMILY, &req);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_netlink_message_append_string(req, CTRL_ATTR_FAMILY_NAME, name);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
@@ -99,62 +204,105 @@ static int lookup_nlmsg_type(sd_netlink *nl, sd_genl_family_t family, uint16_t *
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_netlink_message_read_u16(reply, CTRL_ATTR_FAMILY_ID, &u);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = hashmap_ensure_put(&nl->genl_family_to_nlmsg_type, NULL, INT_TO_PTR(family), UINT_TO_PTR(u));
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = hashmap_ensure_put(&nl->nlmsg_type_to_genl_family, NULL, UINT_TO_PTR(u), INT_TO_PTR(family));
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
*ret = u;
|
||||
return 0;
|
||||
return genl_family_new(nl, name, type_system, reply, ret);
|
||||
}
|
||||
|
||||
int sd_genl_message_new(sd_netlink *nl, sd_genl_family_t family, uint8_t cmd, sd_netlink_message **ret) {
|
||||
uint16_t nlmsg_type = 0; /* Unnecessary initialization to appease gcc */
|
||||
static int genl_family_get_by_name(sd_netlink *nl, const char *name, const GenericNetlinkFamily **ret) {
|
||||
const GenericNetlinkFamily *f, *ctrl;
|
||||
int r;
|
||||
|
||||
assert_return(nl, -EINVAL);
|
||||
assert_return(nl->protocol == NETLINK_GENERIC, -EINVAL);
|
||||
assert_return(ret, -EINVAL);
|
||||
assert(nl);
|
||||
assert(nl->protocol == NETLINK_GENERIC);
|
||||
assert(name);
|
||||
assert(ret);
|
||||
|
||||
r = lookup_nlmsg_type(nl, family, &nlmsg_type);
|
||||
if (r < 0)
|
||||
return r;
|
||||
f = hashmap_get(nl->genl_family_by_name, name);
|
||||
if (f) {
|
||||
if (f->id == 0) /* kernel does not support the family. */
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
return genl_message_new(nl, family, nlmsg_type, cmd, ret);
|
||||
*ret = f;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (streq(name, CTRL_GENL_NAME))
|
||||
return genl_family_get_by_name_internal(nl, &nlctrl_static, CTRL_GENL_NAME, ret);
|
||||
|
||||
ctrl = hashmap_get(nl->genl_family_by_name, CTRL_GENL_NAME);
|
||||
if (!ctrl) {
|
||||
r = genl_family_get_by_name_internal(nl, &nlctrl_static, CTRL_GENL_NAME, &ctrl);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
return genl_family_get_by_name_internal(nl, ctrl, name, ret);
|
||||
}
|
||||
|
||||
int nlmsg_type_to_genl_family(const sd_netlink *nl, uint16_t nlmsg_type, sd_genl_family_t *ret) {
|
||||
void *p;
|
||||
static int genl_family_get_by_id(sd_netlink *nl, uint16_t id, const GenericNetlinkFamily **ret) {
|
||||
const GenericNetlinkFamily *f;
|
||||
|
||||
assert(nl);
|
||||
assert(nl->protocol == NETLINK_GENERIC);
|
||||
assert(ret);
|
||||
|
||||
if (nlmsg_type == NLMSG_ERROR)
|
||||
*ret = SD_GENL_ERROR;
|
||||
else if (nlmsg_type == NLMSG_DONE)
|
||||
*ret = SD_GENL_DONE;
|
||||
else if (nlmsg_type == GENL_ID_CTRL)
|
||||
*ret = SD_GENL_ID_CTRL;
|
||||
else {
|
||||
p = hashmap_get(nl->nlmsg_type_to_genl_family, UINT_TO_PTR(nlmsg_type));
|
||||
if (!p)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
*ret = PTR_TO_INT(p);
|
||||
f = hashmap_get(nl->genl_family_by_id, UINT_TO_PTR(id));
|
||||
if (f) {
|
||||
*ret = f;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (id == GENL_ID_CTRL) {
|
||||
*ret = &nlctrl_static;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
int genl_get_type_system_and_header_size(
|
||||
sd_netlink *nl,
|
||||
uint16_t id,
|
||||
const NLTypeSystem **ret_type_system,
|
||||
size_t *ret_header_size) {
|
||||
|
||||
const GenericNetlinkFamily *f;
|
||||
int r;
|
||||
|
||||
assert(nl);
|
||||
assert(nl->protocol == NETLINK_GENERIC);
|
||||
|
||||
r = genl_family_get_by_id(nl, id, &f);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (ret_type_system) {
|
||||
r = genl_family_get_type_system(f, ret_type_system);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
if (ret_header_size)
|
||||
*ret_header_size = sizeof(struct genlmsghdr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sd_genl_message_get_family(sd_netlink *nl, sd_netlink_message *m, sd_genl_family_t *ret) {
|
||||
int sd_genl_message_new(sd_netlink *nl, const char *family_name, uint8_t cmd, sd_netlink_message **ret) {
|
||||
const GenericNetlinkFamily *family;
|
||||
int r;
|
||||
|
||||
assert_return(nl, -EINVAL);
|
||||
assert_return(nl->protocol == NETLINK_GENERIC, -EINVAL);
|
||||
assert_return(family_name, -EINVAL);
|
||||
assert_return(ret, -EINVAL);
|
||||
|
||||
r = genl_family_get_by_name(nl, family_name, &family);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
return genl_message_new(nl, family, cmd, ret);
|
||||
}
|
||||
|
||||
int sd_genl_message_get_family_name(sd_netlink *nl, sd_netlink_message *m, const char **ret) {
|
||||
const GenericNetlinkFamily *family;
|
||||
uint16_t nlmsg_type;
|
||||
int r;
|
||||
|
||||
@@ -167,5 +315,14 @@ int sd_genl_message_get_family(sd_netlink *nl, sd_netlink_message *m, sd_genl_fa
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
return nlmsg_type_to_genl_family(nl, nlmsg_type, ret);
|
||||
r = genl_family_get_by_id(nl, nlmsg_type, &family);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
*ret = family->name;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sd_genl_socket_open(sd_netlink **ret) {
|
||||
return netlink_open_family(ret, NETLINK_GENERIC);
|
||||
}
|
||||
|
||||
@@ -3,4 +3,6 @@
|
||||
|
||||
#include "sd-netlink.h"
|
||||
|
||||
int nlmsg_type_to_genl_family(const sd_netlink *nl, uint16_t type, sd_genl_family_t *ret);
|
||||
#define CTRL_GENL_NAME "nlctrl"
|
||||
|
||||
void genl_clear_family(sd_netlink *nl);
|
||||
|
||||
@@ -95,8 +95,8 @@ struct sd_netlink {
|
||||
sd_event_source *exit_event_source;
|
||||
sd_event *event;
|
||||
|
||||
Hashmap *genl_family_to_nlmsg_type;
|
||||
Hashmap *nlmsg_type_to_genl_family;
|
||||
Hashmap *genl_family_by_name;
|
||||
Hashmap *genl_family_by_id;
|
||||
};
|
||||
|
||||
struct netlink_attribute {
|
||||
|
||||
@@ -80,20 +80,18 @@ int message_new_full(
|
||||
}
|
||||
|
||||
int message_new(sd_netlink *nl, sd_netlink_message **ret, uint16_t type) {
|
||||
const NLType *nl_type;
|
||||
const NLTypeSystem *type_system;
|
||||
size_t size;
|
||||
int r;
|
||||
|
||||
assert_return(nl, -EINVAL);
|
||||
assert_return(ret, -EINVAL);
|
||||
|
||||
r = type_system_root_get_type(nl, &nl_type, type);
|
||||
r = type_system_root_get_type_system_and_header_size(nl, type, &type_system, &size);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (type_get_type(nl_type) != NETLINK_TYPE_NESTED)
|
||||
return -EINVAL;
|
||||
|
||||
return message_new_full(nl, type, type_get_type_system(nl_type), type_get_size(nl_type), ret);
|
||||
return message_new_full(nl, type, type_system, size, ret);
|
||||
}
|
||||
|
||||
int message_new_synthetic_error(sd_netlink *nl, int error, uint32_t serial, sd_netlink_message **ret) {
|
||||
@@ -1319,8 +1317,6 @@ static int netlink_message_parse_error(sd_netlink_message *m) {
|
||||
}
|
||||
|
||||
int sd_netlink_message_rewind(sd_netlink_message *m, sd_netlink *nl) {
|
||||
const NLType *nl_type;
|
||||
uint16_t type;
|
||||
size_t size;
|
||||
int r;
|
||||
|
||||
@@ -1341,28 +1337,18 @@ int sd_netlink_message_rewind(sd_netlink_message *m, sd_netlink *nl) {
|
||||
|
||||
assert(m->hdr);
|
||||
|
||||
r = type_system_root_get_type(nl, &nl_type, m->hdr->nlmsg_type);
|
||||
r = type_system_root_get_type_system_and_header_size(nl, m->hdr->nlmsg_type,
|
||||
&m->containers[0].type_system, &size);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
type = type_get_type(nl_type);
|
||||
size = type_get_size(nl_type);
|
||||
if (sd_netlink_message_is_error(m))
|
||||
return netlink_message_parse_error(m);
|
||||
|
||||
if (type == NETLINK_TYPE_NESTED) {
|
||||
m->containers[0].type_system = type_get_type_system(nl_type);
|
||||
|
||||
if (sd_netlink_message_is_error(m))
|
||||
r = netlink_message_parse_error(m);
|
||||
else
|
||||
r = netlink_container_parse(m,
|
||||
&m->containers[m->n_containers],
|
||||
(struct rtattr*)((uint8_t*) NLMSG_DATA(m->hdr) + NLMSG_ALIGN(size)),
|
||||
NLMSG_PAYLOAD(m->hdr, size));
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
return 0;
|
||||
return netlink_container_parse(m,
|
||||
&m->containers[0],
|
||||
(struct rtattr*)((uint8_t*) NLMSG_DATA(m->hdr) + NLMSG_ALIGN(size)),
|
||||
NLMSG_PAYLOAD(m->hdr, size));
|
||||
}
|
||||
|
||||
void message_seal(sd_netlink_message *m) {
|
||||
|
||||
@@ -326,7 +326,7 @@ int socket_read_message(sd_netlink *nl) {
|
||||
|
||||
for (struct nlmsghdr *new_msg = nl->rbuffer; NLMSG_OK(new_msg, len) && !done; new_msg = NLMSG_NEXT(new_msg, len)) {
|
||||
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
|
||||
const NLType *nl_type;
|
||||
size_t size;
|
||||
|
||||
if (!group && new_msg->nlmsg_pid != nl->sockaddr.nl.nl_pid)
|
||||
/* not broadcast and not for us */
|
||||
@@ -346,7 +346,7 @@ int socket_read_message(sd_netlink *nl) {
|
||||
}
|
||||
|
||||
/* check that we support this message type */
|
||||
r = type_system_root_get_type(nl, &nl_type, new_msg->nlmsg_type);
|
||||
r = type_system_root_get_type_system_and_header_size(nl, new_msg->nlmsg_type, NULL, &size);
|
||||
if (r < 0) {
|
||||
if (r == -EOPNOTSUPP)
|
||||
log_debug("sd-netlink: ignored message with unknown type: %i",
|
||||
@@ -356,7 +356,7 @@ int socket_read_message(sd_netlink *nl) {
|
||||
}
|
||||
|
||||
/* check that the size matches the message type */
|
||||
if (new_msg->nlmsg_len < NLMSG_LENGTH(type_get_size(nl_type))) {
|
||||
if (new_msg->nlmsg_len < NLMSG_LENGTH(size)) {
|
||||
log_debug("sd-netlink: message is shorter than expected, dropping");
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -44,8 +44,6 @@ static const NLType genl_ctrl_types[] = {
|
||||
[CTRL_ATTR_OP] = { .type = NETLINK_TYPE_U32 },
|
||||
};
|
||||
|
||||
DEFINE_TYPE_SYSTEM(genl_ctrl);
|
||||
|
||||
/***************** genl batadv type systems *****************/
|
||||
static const NLType genl_batadv_types[] = {
|
||||
[BATADV_ATTR_VERSION] = { .type = NETLINK_TYPE_STRING },
|
||||
@@ -110,8 +108,6 @@ static const NLType genl_batadv_types[] = {
|
||||
[BATADV_ATTR_THROUGHPUT_OVERRIDE] = { .type = NETLINK_TYPE_U32 },
|
||||
};
|
||||
|
||||
DEFINE_TYPE_SYSTEM(genl_batadv);
|
||||
|
||||
/***************** genl fou type systems *****************/
|
||||
static const NLType genl_fou_types[] = {
|
||||
[FOU_ATTR_PORT] = { .type = NETLINK_TYPE_U16 },
|
||||
@@ -127,8 +123,6 @@ static const NLType genl_fou_types[] = {
|
||||
[FOU_ATTR_IFINDEX] = { .type = NETLINK_TYPE_U32},
|
||||
};
|
||||
|
||||
DEFINE_TYPE_SYSTEM(genl_fou);
|
||||
|
||||
/***************** genl l2tp type systems *****************/
|
||||
static const NLType genl_l2tp_types[] = {
|
||||
[L2TP_ATTR_PW_TYPE] = { .type = NETLINK_TYPE_U16 },
|
||||
@@ -160,8 +154,6 @@ static const NLType genl_l2tp_types[] = {
|
||||
[L2TP_ATTR_UDP_ZERO_CSUM6_RX] = { .type = NETLINK_TYPE_FLAG },
|
||||
};
|
||||
|
||||
DEFINE_TYPE_SYSTEM(genl_l2tp);
|
||||
|
||||
/***************** genl macsec type systems *****************/
|
||||
static const NLType genl_macsec_rxsc_types[] = {
|
||||
[MACSEC_RXSC_ATTR_SCI] = { .type = NETLINK_TYPE_U64 },
|
||||
@@ -185,8 +177,6 @@ static const NLType genl_macsec_types[] = {
|
||||
[MACSEC_ATTR_SA_CONFIG] = { .type = NETLINK_TYPE_NESTED, .type_system = &genl_macsec_sa_type_system },
|
||||
};
|
||||
|
||||
DEFINE_TYPE_SYSTEM(genl_macsec);
|
||||
|
||||
/***************** genl nl80211 type systems *****************/
|
||||
static const NLType genl_nl80211_types[] = {
|
||||
[NL80211_ATTR_IFINDEX] = { .type = NETLINK_TYPE_U32 },
|
||||
@@ -195,8 +185,6 @@ static const NLType genl_nl80211_types[] = {
|
||||
[NL80211_ATTR_IFTYPE] = { .type = NETLINK_TYPE_U32 },
|
||||
};
|
||||
|
||||
DEFINE_TYPE_SYSTEM(genl_nl80211);
|
||||
|
||||
/***************** genl wireguard type systems *****************/
|
||||
static const NLType genl_wireguard_allowedip_types[] = {
|
||||
[WGALLOWEDIP_A_FAMILY] = { .type = NETLINK_TYPE_U16 },
|
||||
@@ -227,28 +215,20 @@ static const NLType genl_wireguard_types[] = {
|
||||
[WGDEVICE_A_PEERS] = { .type = NETLINK_TYPE_NESTED, .type_system = &genl_wireguard_peer_type_system },
|
||||
};
|
||||
|
||||
DEFINE_TYPE_SYSTEM(genl_wireguard);
|
||||
|
||||
/***************** genl families *****************/
|
||||
static const NLType genl_types[] = {
|
||||
[SD_GENL_ID_CTRL] = { .type = NETLINK_TYPE_NESTED, .type_system = &genl_ctrl_type_system, .size = sizeof(struct genlmsghdr) },
|
||||
[SD_GENL_WIREGUARD] = { .type = NETLINK_TYPE_NESTED, .type_system = &genl_wireguard_type_system, .size = sizeof(struct genlmsghdr) },
|
||||
[SD_GENL_FOU] = { .type = NETLINK_TYPE_NESTED, .type_system = &genl_fou_type_system, .size = sizeof(struct genlmsghdr) },
|
||||
[SD_GENL_L2TP] = { .type = NETLINK_TYPE_NESTED, .type_system = &genl_l2tp_type_system, .size = sizeof(struct genlmsghdr) },
|
||||
[SD_GENL_MACSEC] = { .type = NETLINK_TYPE_NESTED, .type_system = &genl_macsec_type_system, .size = sizeof(struct genlmsghdr) },
|
||||
[SD_GENL_NL80211] = { .type = NETLINK_TYPE_NESTED, .type_system = &genl_nl80211_type_system, .size = sizeof(struct genlmsghdr) },
|
||||
[SD_GENL_BATADV] = { .type = NETLINK_TYPE_NESTED, .type_system = &genl_batadv_type_system, .size = sizeof(struct genlmsghdr) },
|
||||
static const NLTypeSystemUnionElement genl_type_systems[] = {
|
||||
{ .name = CTRL_GENL_NAME, .type_system = TYPE_SYSTEM_FROM_TYPE(genl_ctrl), },
|
||||
{ .name = BATADV_NL_NAME, .type_system = TYPE_SYSTEM_FROM_TYPE(genl_batadv), },
|
||||
{ .name = FOU_GENL_NAME, .type_system = TYPE_SYSTEM_FROM_TYPE(genl_fou), },
|
||||
{ .name = L2TP_GENL_NAME, .type_system = TYPE_SYSTEM_FROM_TYPE(genl_l2tp), },
|
||||
{ .name = MACSEC_GENL_NAME, .type_system = TYPE_SYSTEM_FROM_TYPE(genl_macsec), },
|
||||
{ .name = NL80211_GENL_NAME, .type_system = TYPE_SYSTEM_FROM_TYPE(genl_nl80211), },
|
||||
{ .name = WG_GENL_NAME, .type_system = TYPE_SYSTEM_FROM_TYPE(genl_wireguard), },
|
||||
};
|
||||
|
||||
DEFINE_TYPE_SYSTEM(genl);
|
||||
/* This is the root type system union, so match_attribute is not necessary. */
|
||||
DEFINE_TYPE_SYSTEM_UNION_MATCH_SIBLING(genl, 0);
|
||||
|
||||
int genl_get_type(sd_netlink *genl, uint16_t nlmsg_type, const NLType **ret) {
|
||||
sd_genl_family_t family;
|
||||
int r;
|
||||
|
||||
r = nlmsg_type_to_genl_family(genl, nlmsg_type, &family);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
return type_system_get_type(&genl_type_system, ret, family);
|
||||
int genl_get_type_system_by_name(const char *name, const NLTypeSystem **ret) {
|
||||
return type_system_union_get_type_system_by_string(&genl_type_system_union, ret, name);
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
#include <linux/netlink.h>
|
||||
|
||||
#include "netlink-genl.h"
|
||||
#include "netlink-internal.h"
|
||||
#include "netlink-types-internal.h"
|
||||
|
||||
@@ -54,20 +55,43 @@ uint16_t type_system_get_count(const NLTypeSystem *type_system) {
|
||||
return type_system->count;
|
||||
}
|
||||
|
||||
int type_system_root_get_type(sd_netlink *nl, const NLType **ret, uint16_t type) {
|
||||
if (!nl || IN_SET(type, NLMSG_DONE, NLMSG_ERROR))
|
||||
return type_system_get_type(&basic_type_system, ret, type);
|
||||
int type_system_root_get_type_system_and_header_size(
|
||||
sd_netlink *nl,
|
||||
uint16_t type,
|
||||
const NLTypeSystem **ret_type_system,
|
||||
size_t *ret_header_size) {
|
||||
|
||||
switch(nl->protocol) {
|
||||
case NETLINK_ROUTE:
|
||||
return rtnl_get_type(type, ret);
|
||||
case NETLINK_NETFILTER:
|
||||
return nfnl_get_type(type, ret);
|
||||
case NETLINK_GENERIC:
|
||||
return genl_get_type(nl, type, ret);
|
||||
default:
|
||||
const NLType *nl_type;
|
||||
int r;
|
||||
|
||||
assert(nl);
|
||||
|
||||
if (IN_SET(type, NLMSG_DONE, NLMSG_ERROR))
|
||||
r = type_system_get_type(&basic_type_system, &nl_type, type);
|
||||
else
|
||||
switch(nl->protocol) {
|
||||
case NETLINK_ROUTE:
|
||||
r = rtnl_get_type(type, &nl_type);
|
||||
break;
|
||||
case NETLINK_NETFILTER:
|
||||
r = nfnl_get_type(type, &nl_type);
|
||||
break;
|
||||
case NETLINK_GENERIC:
|
||||
return genl_get_type_system_and_header_size(nl, type, ret_type_system, ret_header_size);
|
||||
default:
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (type_get_type(nl_type) != NETLINK_TYPE_NESTED)
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
if (ret_type_system)
|
||||
*ret_type_system = type_get_type_system(nl_type);
|
||||
if (ret_header_size)
|
||||
*ret_header_size = type_get_size(nl_type);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int type_system_get_type(const NLTypeSystem *type_system, const NLType **ret, uint16_t type) {
|
||||
|
||||
@@ -37,15 +37,25 @@ typedef struct NLType NLType;
|
||||
|
||||
int rtnl_get_type(uint16_t nlmsg_type, const NLType **ret);
|
||||
int nfnl_get_type(uint16_t nlmsg_type, const NLType **ret);
|
||||
int genl_get_type(sd_netlink *genl, uint16_t nlmsg_type, const NLType **ret);
|
||||
int genl_get_type_system_by_name(const char *name, const NLTypeSystem **ret);
|
||||
int genl_get_type_system_and_header_size(
|
||||
sd_netlink *nl,
|
||||
uint16_t id,
|
||||
const NLTypeSystem **ret_type_system,
|
||||
size_t *ret_header_size);
|
||||
|
||||
uint16_t type_get_type(const NLType *type);
|
||||
size_t type_get_size(const NLType *type);
|
||||
const NLTypeSystem *type_get_type_system(const NLType *type);
|
||||
const NLTypeSystemUnion *type_get_type_system_union(const NLType *type);
|
||||
|
||||
int type_system_root_get_type_system_and_header_size(
|
||||
sd_netlink *nl,
|
||||
uint16_t type,
|
||||
const NLTypeSystem **ret_type_system,
|
||||
size_t *ret_header_size);
|
||||
|
||||
uint16_t type_system_get_count(const NLTypeSystem *type_system);
|
||||
int type_system_root_get_type(sd_netlink *nl, const NLType **ret, uint16_t type);
|
||||
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);
|
||||
int type_system_get_type_system_union(const NLTypeSystem *type_system, const NLTypeSystemUnion **ret, uint16_t type);
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include "hashmap.h"
|
||||
#include "io-util.h"
|
||||
#include "macro.h"
|
||||
#include "netlink-genl.h"
|
||||
#include "netlink-internal.h"
|
||||
#include "netlink-slot.h"
|
||||
#include "process-util.h"
|
||||
@@ -196,8 +197,7 @@ static sd_netlink *netlink_free(sd_netlink *nl) {
|
||||
|
||||
hashmap_free(nl->broadcast_group_refs);
|
||||
|
||||
hashmap_free(nl->genl_family_to_nlmsg_type);
|
||||
hashmap_free(nl->nlmsg_type_to_genl_family);
|
||||
genl_clear_family(nl);
|
||||
|
||||
safe_close(nl->fd);
|
||||
return mfree(nl);
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include "alloc-util.h"
|
||||
#include "ether-addr-util.h"
|
||||
#include "macro.h"
|
||||
#include "netlink-genl.h"
|
||||
#include "netlink-internal.h"
|
||||
#include "netlink-util.h"
|
||||
#include "socket-util.h"
|
||||
@@ -514,7 +515,7 @@ static void test_array(void) {
|
||||
log_debug("/* %s */", __func__);
|
||||
|
||||
assert_se(sd_genl_socket_open(&genl) >= 0);
|
||||
assert_se(sd_genl_message_new(genl, SD_GENL_ID_CTRL, CTRL_CMD_GETFAMILY, &m) >= 0);
|
||||
assert_se(sd_genl_message_new(genl, CTRL_GENL_NAME, CTRL_CMD_GETFAMILY, &m) >= 0);
|
||||
|
||||
assert_se(sd_netlink_message_open_container(m, CTRL_ATTR_MCAST_GROUPS) >= 0);
|
||||
for (unsigned i = 0; i < 10; i++) {
|
||||
@@ -582,6 +583,23 @@ static void test_strv(sd_netlink *rtnl) {
|
||||
assert_se(sd_netlink_message_exit_container(m) >= 0);
|
||||
}
|
||||
|
||||
static void test_genl(void) {
|
||||
_cleanup_(sd_netlink_unrefp) sd_netlink *genl = NULL;
|
||||
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
|
||||
const char *name;
|
||||
|
||||
log_debug("/* %s */", __func__);
|
||||
|
||||
assert_se(sd_genl_socket_open(&genl) >= 0);
|
||||
assert_se(sd_genl_message_new(genl, CTRL_GENL_NAME, CTRL_CMD_GETFAMILY, &m) >= 0);
|
||||
assert_se(sd_genl_message_get_family_name(genl, m, &name) >= 0);
|
||||
assert_se(streq(name, CTRL_GENL_NAME));
|
||||
|
||||
m = sd_netlink_message_unref(m);
|
||||
assert_se(sd_genl_message_new(genl, "should-not-exist", CTRL_CMD_GETFAMILY, &m) < 0);
|
||||
assert_se(sd_genl_message_new(genl, "should-not-exist", CTRL_CMD_GETFAMILY, &m) == -EOPNOTSUPP);
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
sd_netlink *rtnl;
|
||||
sd_netlink_message *m;
|
||||
@@ -642,5 +660,7 @@ int main(void) {
|
||||
assert_se((r = sd_netlink_message_unref(r)) == NULL);
|
||||
assert_se((rtnl = sd_netlink_unref(rtnl)) == NULL);
|
||||
|
||||
test_genl();
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -122,7 +122,7 @@ static int netdev_batadv_post_create(NetDev *netdev, Link *link, sd_netlink_mess
|
||||
b = BATADV(netdev);
|
||||
assert(b);
|
||||
|
||||
r = sd_genl_message_new(netdev->manager->genl, SD_GENL_BATADV, BATADV_CMD_SET_MESH, &message);
|
||||
r = sd_genl_message_new(netdev->manager->genl, BATADV_NL_NAME, BATADV_CMD_SET_MESH, &message);
|
||||
if (r < 0)
|
||||
return log_netdev_error_errno(netdev, r, "Failed to allocate generic netlink message: %m");
|
||||
|
||||
|
||||
@@ -36,7 +36,7 @@ static int netdev_fill_fou_tunnel_message(NetDev *netdev, sd_netlink_message **r
|
||||
|
||||
assert(t);
|
||||
|
||||
r = sd_genl_message_new(netdev->manager->genl, SD_GENL_FOU, FOU_CMD_ADD, &m);
|
||||
r = sd_genl_message_new(netdev->manager->genl, FOU_GENL_NAME, FOU_CMD_ADD, &m);
|
||||
if (r < 0)
|
||||
return log_netdev_error_errno(netdev, r, "Failed to allocate generic netlink message: %m");
|
||||
|
||||
|
||||
@@ -104,7 +104,7 @@ static int netdev_l2tp_fill_message_tunnel(NetDev *netdev, union in_addr_union *
|
||||
|
||||
assert(t);
|
||||
|
||||
r = sd_genl_message_new(netdev->manager->genl, SD_GENL_L2TP, L2TP_CMD_TUNNEL_CREATE, &m);
|
||||
r = sd_genl_message_new(netdev->manager->genl, L2TP_GENL_NAME, L2TP_CMD_TUNNEL_CREATE, &m);
|
||||
if (r < 0)
|
||||
return log_netdev_error_errno(netdev, r, "Failed to create generic netlink message: %m");
|
||||
|
||||
@@ -195,7 +195,7 @@ static int netdev_l2tp_fill_message_session(NetDev *netdev, L2tpSession *session
|
||||
assert(session);
|
||||
assert(session->tunnel);
|
||||
|
||||
r = sd_genl_message_new(netdev->manager->genl, SD_GENL_L2TP, L2TP_CMD_SESSION_CREATE, &m);
|
||||
r = sd_genl_message_new(netdev->manager->genl, L2TP_GENL_NAME, L2TP_CMD_SESSION_CREATE, &m);
|
||||
if (r < 0)
|
||||
return log_netdev_error_errno(netdev, r, "Failed to create generic netlink message: %m");
|
||||
|
||||
|
||||
@@ -224,7 +224,7 @@ static int netdev_macsec_fill_message(NetDev *netdev, int command, sd_netlink_me
|
||||
assert(netdev);
|
||||
assert(netdev->ifindex > 0);
|
||||
|
||||
r = sd_genl_message_new(netdev->manager->genl, SD_GENL_MACSEC, command, &m);
|
||||
r = sd_genl_message_new(netdev->manager->genl, MACSEC_GENL_NAME, command, &m);
|
||||
if (r < 0)
|
||||
return log_netdev_error_errno(netdev, r, "Failed to create generic netlink message: %m");
|
||||
|
||||
|
||||
@@ -229,7 +229,7 @@ static int wireguard_set_interface(NetDev *netdev) {
|
||||
|
||||
message = sd_netlink_message_unref(message);
|
||||
|
||||
r = sd_genl_message_new(netdev->manager->genl, SD_GENL_WIREGUARD, WG_CMD_SET_DEVICE, &message);
|
||||
r = sd_genl_message_new(netdev->manager->genl, WG_GENL_NAME, WG_CMD_SET_DEVICE, &message);
|
||||
if (r < 0)
|
||||
return log_netdev_error_errno(netdev, r, "Failed to allocate generic netlink message: %m");
|
||||
|
||||
|
||||
@@ -46,7 +46,6 @@ int main(int argc, char **argv) {
|
||||
assert_cc(sizeof(sd_lldp_event_t) == sizeof(int64_t));
|
||||
assert_cc(sizeof(sd_ndisc_event_t) == sizeof(int64_t));
|
||||
assert_cc(sizeof(sd_dhcp_lease_server_type_t) == sizeof(int64_t));
|
||||
assert_cc(sizeof(sd_genl_family_t) == sizeof(int64_t));
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
+11
-10
@@ -1,17 +1,18 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
|
||||
#include "log.h"
|
||||
#include "string-util.h"
|
||||
#include "wifi-util.h"
|
||||
|
||||
int wifi_get_interface(sd_netlink *genl, int ifindex, enum nl80211_iftype *iftype, char **ssid) {
|
||||
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL, *reply = NULL;
|
||||
sd_genl_family_t family;
|
||||
const char *family;
|
||||
int r;
|
||||
|
||||
assert(genl);
|
||||
assert(ifindex > 0);
|
||||
|
||||
r = sd_genl_message_new(genl, SD_GENL_NL80211, NL80211_CMD_GET_INTERFACE, &m);
|
||||
r = sd_genl_message_new(genl, NL80211_GENL_NAME, NL80211_CMD_GET_INTERFACE, &m);
|
||||
if (r < 0)
|
||||
return log_debug_errno(r, "Failed to create generic netlink message: %m");
|
||||
|
||||
@@ -38,11 +39,11 @@ int wifi_get_interface(sd_netlink *genl, int ifindex, enum nl80211_iftype *iftyp
|
||||
if (r < 0)
|
||||
return log_debug_errno(r, "Failed to get information about wifi interface %d: %m", ifindex);
|
||||
|
||||
r = sd_genl_message_get_family(genl, reply, &family);
|
||||
r = sd_genl_message_get_family_name(genl, reply, &family);
|
||||
if (r < 0)
|
||||
return log_debug_errno(r, "Failed to determine genl family: %m");
|
||||
if (family != SD_GENL_NL80211) {
|
||||
log_debug("Received message of unexpected genl family %" PRIi64 ", ignoring.", family);
|
||||
if (!streq(family, NL80211_GENL_NAME)) {
|
||||
log_debug("Received message of unexpected genl family '%s', ignoring.", family);
|
||||
goto nodata;
|
||||
}
|
||||
|
||||
@@ -75,14 +76,14 @@ nodata:
|
||||
|
||||
int wifi_get_station(sd_netlink *genl, int ifindex, struct ether_addr *bssid) {
|
||||
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL, *reply = NULL;
|
||||
sd_genl_family_t family;
|
||||
const char *family;
|
||||
int r;
|
||||
|
||||
assert(genl);
|
||||
assert(ifindex > 0);
|
||||
assert(bssid);
|
||||
|
||||
r = sd_genl_message_new(genl, SD_GENL_NL80211, NL80211_CMD_GET_STATION, &m);
|
||||
r = sd_genl_message_new(genl, NL80211_GENL_NAME, NL80211_CMD_GET_STATION, &m);
|
||||
if (r < 0)
|
||||
return log_debug_errno(r, "Failed to create generic netlink message: %m");
|
||||
|
||||
@@ -106,11 +107,11 @@ int wifi_get_station(sd_netlink *genl, int ifindex, struct ether_addr *bssid) {
|
||||
if (r < 0)
|
||||
return log_debug_errno(r, "Failed to get information about wifi station: %m");
|
||||
|
||||
r = sd_genl_message_get_family(genl, reply, &family);
|
||||
r = sd_genl_message_get_family_name(genl, reply, &family);
|
||||
if (r < 0)
|
||||
return log_debug_errno(r, "Failed to determine genl family: %m");
|
||||
if (family != SD_GENL_NL80211) {
|
||||
log_debug("Received message of unexpected genl family %" PRIi64 ", ignoring.", family);
|
||||
if (!streq(family, NL80211_GENL_NAME)) {
|
||||
log_debug("Received message of unexpected genl family '%s', ignoring.", family);
|
||||
goto nodata;
|
||||
}
|
||||
|
||||
|
||||
@@ -34,21 +34,6 @@ typedef struct sd_netlink sd_netlink;
|
||||
typedef struct sd_netlink_message sd_netlink_message;
|
||||
typedef struct sd_netlink_slot sd_netlink_slot;
|
||||
|
||||
typedef enum sd_genl_family_t {
|
||||
SD_GENL_ERROR,
|
||||
SD_GENL_DONE,
|
||||
SD_GENL_ID_CTRL,
|
||||
SD_GENL_WIREGUARD,
|
||||
SD_GENL_FOU,
|
||||
SD_GENL_L2TP,
|
||||
SD_GENL_MACSEC,
|
||||
SD_GENL_NL80211,
|
||||
SD_GENL_BATADV,
|
||||
_SD_GENL_FAMILY_MAX,
|
||||
_SD_GENL_FAMILY_INVALID = -EINVAL,
|
||||
_SD_ENUM_FORCE_S64(GENL_FAMILY)
|
||||
} sd_genl_family_t;
|
||||
|
||||
/* callback */
|
||||
typedef int (*sd_netlink_message_handler_t)(sd_netlink *nl, sd_netlink_message *m, void *userdata);
|
||||
typedef _sd_destroy_t sd_netlink_destroy_t;
|
||||
@@ -250,8 +235,8 @@ int sd_nfnl_nft_message_add_setelem_end(sd_netlink_message *m);
|
||||
|
||||
/* genl */
|
||||
int sd_genl_socket_open(sd_netlink **ret);
|
||||
int sd_genl_message_new(sd_netlink *genl, sd_genl_family_t family, uint8_t cmd, sd_netlink_message **ret);
|
||||
int sd_genl_message_get_family(sd_netlink *genl, sd_netlink_message *m, sd_genl_family_t *ret);
|
||||
int sd_genl_message_new(sd_netlink *genl, const char *family_name, uint8_t cmd, sd_netlink_message **ret);
|
||||
int sd_genl_message_get_family_name(sd_netlink *genl, sd_netlink_message *m, const char **ret);
|
||||
|
||||
/* slot */
|
||||
sd_netlink_slot *sd_netlink_slot_ref(sd_netlink_slot *slot);
|
||||
|
||||
Reference in New Issue
Block a user