You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
410 lines
8.8 KiB
410 lines
8.8 KiB
/* SPDX-License-Identifier: LGPL-2.1-only */
|
|
/*
|
|
* lib/genl/family.c Generic Netlink Family
|
|
*
|
|
* This library 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 version 2.1
|
|
* of the License.
|
|
*
|
|
* Copyright (c) 2003-2012 Thomas Graf <tgraf@suug.ch>
|
|
*/
|
|
|
|
/**
|
|
* @ingroup genl_ctrl
|
|
* @defgroup genl_family Generic Netlink Family Object
|
|
*
|
|
* Object representing a kernel side registered Generic Netlink family
|
|
*
|
|
* @{
|
|
*/
|
|
|
|
#include <netlink-private/genl.h>
|
|
#include <netlink/netlink.h>
|
|
#include <netlink/genl/genl.h>
|
|
#include <netlink/genl/family.h>
|
|
#include <netlink/utils.h>
|
|
|
|
#include "netlink-private/utils.h"
|
|
|
|
/** @cond SKIP */
|
|
#define FAMILY_ATTR_ID 0x01
|
|
#define FAMILY_ATTR_NAME 0x02
|
|
#define FAMILY_ATTR_VERSION 0x04
|
|
#define FAMILY_ATTR_HDRSIZE 0x08
|
|
#define FAMILY_ATTR_MAXATTR 0x10
|
|
#define FAMILY_ATTR_OPS 0x20
|
|
|
|
struct nl_object_ops genl_family_ops;
|
|
|
|
static void family_constructor(struct nl_object *c)
|
|
{
|
|
struct genl_family *family = (struct genl_family *) c;
|
|
|
|
nl_init_list_head(&family->gf_ops);
|
|
nl_init_list_head(&family->gf_mc_grps);
|
|
}
|
|
|
|
static void family_free_data(struct nl_object *c)
|
|
{
|
|
struct genl_family *family = (struct genl_family *) c;
|
|
struct genl_family_op *ops, *tmp;
|
|
struct genl_family_grp *grp, *t_grp;
|
|
|
|
if (family == NULL)
|
|
return;
|
|
|
|
nl_list_for_each_entry_safe(ops, tmp, &family->gf_ops, o_list) {
|
|
nl_list_del(&ops->o_list);
|
|
free(ops);
|
|
}
|
|
|
|
nl_list_for_each_entry_safe(grp, t_grp, &family->gf_mc_grps, list) {
|
|
nl_list_del(&grp->list);
|
|
free(grp);
|
|
}
|
|
|
|
}
|
|
|
|
static int family_clone(struct nl_object *_dst, struct nl_object *_src)
|
|
{
|
|
struct genl_family *dst = nl_object_priv(_dst);
|
|
struct genl_family *src = nl_object_priv(_src);
|
|
struct genl_family_op *ops;
|
|
struct genl_family_grp *grp;
|
|
int err;
|
|
|
|
nl_list_for_each_entry(ops, &src->gf_ops, o_list) {
|
|
err = genl_family_add_op(dst, ops->o_id, ops->o_flags);
|
|
if (err < 0)
|
|
return err;
|
|
}
|
|
|
|
nl_list_for_each_entry(grp, &src->gf_mc_grps, list) {
|
|
err = genl_family_add_grp(dst, grp->id, grp->name);
|
|
if (err < 0)
|
|
return err;
|
|
}
|
|
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void family_dump_line(struct nl_object *obj, struct nl_dump_params *p)
|
|
{
|
|
struct genl_family *family = (struct genl_family *) obj;
|
|
|
|
nl_dump(p, "0x%04x %s version %u\n",
|
|
family->gf_id, family->gf_name, family->gf_version);
|
|
}
|
|
|
|
static const struct trans_tbl ops_flags[] = {
|
|
__ADD(GENL_ADMIN_PERM, admin_perm),
|
|
__ADD(GENL_CMD_CAP_DO, has_doit),
|
|
__ADD(GENL_CMD_CAP_DUMP, has_dump),
|
|
__ADD(GENL_CMD_CAP_HASPOL, has_policy),
|
|
};
|
|
|
|
static char *ops_flags2str(int flags, char *buf, size_t len)
|
|
{
|
|
return __flags2str(flags, buf, len, ops_flags, ARRAY_SIZE(ops_flags));
|
|
}
|
|
|
|
static void family_dump_details(struct nl_object *obj, struct nl_dump_params *p)
|
|
{
|
|
struct genl_family_grp *grp;
|
|
struct genl_family *family = (struct genl_family *) obj;
|
|
|
|
family_dump_line(obj, p);
|
|
nl_dump_line(p, " hdrsize %u maxattr %u\n",
|
|
family->gf_hdrsize, family->gf_maxattr);
|
|
|
|
if (family->ce_mask & FAMILY_ATTR_OPS) {
|
|
struct genl_family_op *op;
|
|
char buf[64];
|
|
|
|
nl_list_for_each_entry(op, &family->gf_ops, o_list) {
|
|
ops_flags2str(op->o_flags, buf, sizeof(buf));
|
|
|
|
genl_op2name(family->gf_id, op->o_id, buf, sizeof(buf));
|
|
|
|
nl_dump_line(p, " op %s (0x%02x)", buf, op->o_id);
|
|
|
|
if (op->o_flags)
|
|
nl_dump(p, " <%s>",
|
|
ops_flags2str(op->o_flags, buf,
|
|
sizeof(buf)));
|
|
|
|
nl_dump(p, "\n");
|
|
}
|
|
}
|
|
|
|
nl_list_for_each_entry(grp, &family->gf_mc_grps, list) {
|
|
nl_dump_line(p, " grp %s (0x%02x)\n", grp->name, grp->id);
|
|
}
|
|
|
|
}
|
|
|
|
static void family_dump_stats(struct nl_object *obj, struct nl_dump_params *p)
|
|
{
|
|
family_dump_details(obj, p);
|
|
}
|
|
|
|
static uint64_t family_compare(struct nl_object *_a, struct nl_object *_b,
|
|
uint64_t attrs, int flags)
|
|
{
|
|
struct genl_family *a = (struct genl_family *) _a;
|
|
struct genl_family *b = (struct genl_family *) _b;
|
|
uint64_t diff = 0;
|
|
|
|
#define FAM_DIFF(ATTR, EXPR) ATTR_DIFF(attrs, FAMILY_ATTR_##ATTR, a, b, EXPR)
|
|
|
|
diff |= FAM_DIFF(ID, a->gf_id != b->gf_id);
|
|
diff |= FAM_DIFF(VERSION, a->gf_version != b->gf_version);
|
|
diff |= FAM_DIFF(HDRSIZE, a->gf_hdrsize != b->gf_hdrsize);
|
|
diff |= FAM_DIFF(MAXATTR, a->gf_maxattr != b->gf_maxattr);
|
|
diff |= FAM_DIFF(NAME, strcmp(a->gf_name, b->gf_name));
|
|
|
|
#undef FAM_DIFF
|
|
|
|
return diff;
|
|
}
|
|
/** @endcond */
|
|
|
|
/**
|
|
* @name Object Allocation
|
|
* @{
|
|
*/
|
|
|
|
/**
|
|
* Allocate new Generic Netlink family object
|
|
*
|
|
* @return Newly allocated Generic Netlink family object or NULL.
|
|
*/
|
|
struct genl_family *genl_family_alloc(void)
|
|
{
|
|
return (struct genl_family *) nl_object_alloc(&genl_family_ops);
|
|
}
|
|
|
|
/**
|
|
* Release reference on Generic Netlink family object
|
|
* @arg family Generic Netlink family object
|
|
*
|
|
* Reduces the reference counter of a Generic Netlink family object by one.
|
|
* The object is freed after the last user has returned its reference.
|
|
*
|
|
* @see nl_object_put()
|
|
*/
|
|
void genl_family_put(struct genl_family *family)
|
|
{
|
|
nl_object_put((struct nl_object *) family);
|
|
}
|
|
|
|
/** @} */
|
|
|
|
/**
|
|
* @name Numeric Identifier
|
|
* @{
|
|
*/
|
|
|
|
/**
|
|
* Return numeric identifier
|
|
* @arg family Generic Netlink family object
|
|
*
|
|
* @return Numeric identifier or 0 if not available.
|
|
*/
|
|
unsigned int genl_family_get_id(struct genl_family *family)
|
|
{
|
|
if (family->ce_mask & FAMILY_ATTR_ID)
|
|
return family->gf_id;
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Set the numeric identifier
|
|
* @arg family Generic Netlink family object
|
|
* @arg id New numeric identifier
|
|
*/
|
|
void genl_family_set_id(struct genl_family *family, unsigned int id)
|
|
{
|
|
family->gf_id = id;
|
|
family->ce_mask |= FAMILY_ATTR_ID;
|
|
}
|
|
|
|
/** @} */
|
|
|
|
/**
|
|
* @name Human Readable Name
|
|
* @{
|
|
*/
|
|
|
|
/**
|
|
* Return human readable name
|
|
* @arg family Generic Netlink family object
|
|
*
|
|
* @return Name of family or NULL if not available
|
|
*/
|
|
char *genl_family_get_name(struct genl_family *family)
|
|
{
|
|
if (family->ce_mask & FAMILY_ATTR_NAME)
|
|
return family->gf_name;
|
|
else
|
|
return NULL;
|
|
}
|
|
|
|
/**
|
|
* Set human readable name
|
|
* @arg family Generic Netlink family object
|
|
* @arg name New human readable name
|
|
*/
|
|
void genl_family_set_name(struct genl_family *family, const char *name)
|
|
{
|
|
strncpy(family->gf_name, name, GENL_NAMSIZ-1);
|
|
family->ce_mask |= FAMILY_ATTR_NAME;
|
|
}
|
|
|
|
/**
|
|
* @name Interface Version
|
|
* @{
|
|
*/
|
|
|
|
/**
|
|
* Return interface version
|
|
* @arg family Generic Netlink family object
|
|
*
|
|
* @return Interface version or 0 if not available.
|
|
*/
|
|
uint8_t genl_family_get_version(struct genl_family *family)
|
|
{
|
|
if (family->ce_mask & FAMILY_ATTR_VERSION)
|
|
return family->gf_version;
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Set interface version
|
|
* @arg family Generic Netlink family object
|
|
* @arg version New interface version
|
|
*/
|
|
void genl_family_set_version(struct genl_family *family, uint8_t version)
|
|
{
|
|
family->gf_version = version;
|
|
family->ce_mask |= FAMILY_ATTR_VERSION;
|
|
}
|
|
|
|
/** @} */
|
|
|
|
/**
|
|
* @name Header Size
|
|
* @{
|
|
*/
|
|
|
|
/**
|
|
* Return user header size expected by kernel component
|
|
* @arg family Generic Netlink family object
|
|
*
|
|
* @return Expected header length or 0 if not available.
|
|
*/
|
|
uint32_t genl_family_get_hdrsize(struct genl_family *family)
|
|
{
|
|
if (family->ce_mask & FAMILY_ATTR_HDRSIZE)
|
|
return family->gf_hdrsize;
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
void genl_family_set_hdrsize(struct genl_family *family, uint32_t hdrsize)
|
|
{
|
|
family->gf_hdrsize = hdrsize;
|
|
family->ce_mask |= FAMILY_ATTR_HDRSIZE;
|
|
}
|
|
|
|
/** @} */
|
|
|
|
/**
|
|
* @name Maximum Expected Attribute
|
|
* @{
|
|
*/
|
|
|
|
uint32_t genl_family_get_maxattr(struct genl_family *family)
|
|
{
|
|
if (family->ce_mask & FAMILY_ATTR_MAXATTR)
|
|
return family->gf_maxattr;
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
void genl_family_set_maxattr(struct genl_family *family, uint32_t maxattr)
|
|
{
|
|
family->gf_maxattr = maxattr;
|
|
family->ce_mask |= FAMILY_ATTR_MAXATTR;
|
|
}
|
|
|
|
/** @} */
|
|
|
|
/**
|
|
* @name Operations
|
|
* @{
|
|
*/
|
|
|
|
int genl_family_add_op(struct genl_family *family, int id, int flags)
|
|
{
|
|
struct genl_family_op *op;
|
|
|
|
op = calloc(1, sizeof(*op));
|
|
if (op == NULL)
|
|
return -NLE_NOMEM;
|
|
|
|
op->o_id = id;
|
|
op->o_flags = flags;
|
|
|
|
nl_list_add_tail(&op->o_list, &family->gf_ops);
|
|
family->ce_mask |= FAMILY_ATTR_OPS;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int genl_family_add_grp(struct genl_family *family, uint32_t id,
|
|
const char *name)
|
|
{
|
|
struct genl_family_grp *grp;
|
|
|
|
if ( !name
|
|
|| strlen (name) >= GENL_NAMSIZ)
|
|
return -NLE_INVAL;
|
|
|
|
grp = calloc(1, sizeof(*grp));
|
|
if (grp == NULL)
|
|
return -NLE_NOMEM;
|
|
|
|
grp->id = id;
|
|
_nl_strncpy(grp->name, name, GENL_NAMSIZ);
|
|
|
|
nl_list_add_tail(&grp->list, &family->gf_mc_grps);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/** @} */
|
|
|
|
/** @cond SKIP */
|
|
struct nl_object_ops genl_family_ops = {
|
|
.oo_name = "genl/family",
|
|
.oo_size = sizeof(struct genl_family),
|
|
.oo_constructor = family_constructor,
|
|
.oo_free_data = family_free_data,
|
|
.oo_clone = family_clone,
|
|
.oo_dump = {
|
|
[NL_DUMP_LINE] = family_dump_line,
|
|
[NL_DUMP_DETAILS] = family_dump_details,
|
|
[NL_DUMP_STATS] = family_dump_stats,
|
|
},
|
|
.oo_compare = family_compare,
|
|
.oo_id_attrs = FAMILY_ATTR_ID,
|
|
};
|
|
/** @endcond */
|
|
|
|
/** @} */
|