1/*
2 * lib/genl/family.c		Generic Netlink Family
3 *
4 *	This library is free software; you can redistribute it and/or
5 *	modify it under the terms of the GNU Lesser General Public
6 *	License as published by the Free Software Foundation version 2.1
7 *	of the License.
8 *
9 * Copyright (c) 2003-2012 Thomas Graf <tgraf@suug.ch>
10 */
11
12/**
13 * @ingroup genl_ctrl
14 * @defgroup genl_family Generic Netlink Family Object
15 *
16 * Object representing a kernel side registered Generic Netlink family
17 *
18 * @{
19 */
20
21#include <netlink-private/genl.h>
22#include <netlink/netlink.h>
23#include <netlink/genl/genl.h>
24#include <netlink/genl/family.h>
25#include <netlink/utils.h>
26
27/** @cond SKIP */
28#define FAMILY_ATTR_ID		0x01
29#define FAMILY_ATTR_NAME	0x02
30#define FAMILY_ATTR_VERSION	0x04
31#define FAMILY_ATTR_HDRSIZE	0x08
32#define FAMILY_ATTR_MAXATTR	0x10
33#define FAMILY_ATTR_OPS		0x20
34
35struct nl_object_ops genl_family_ops;
36
37static void family_constructor(struct nl_object *c)
38{
39	struct genl_family *family = (struct genl_family *) c;
40
41	nl_init_list_head(&family->gf_ops);
42	nl_init_list_head(&family->gf_mc_grps);
43}
44
45static void family_free_data(struct nl_object *c)
46{
47	struct genl_family *family = (struct genl_family *) c;
48	struct genl_family_op *ops, *tmp;
49	struct genl_family_grp *grp, *t_grp;
50
51	if (family == NULL)
52		return;
53
54	nl_list_for_each_entry_safe(ops, tmp, &family->gf_ops, o_list) {
55		nl_list_del(&ops->o_list);
56		free(ops);
57	}
58
59	nl_list_for_each_entry_safe(grp, t_grp, &family->gf_mc_grps, list) {
60		nl_list_del(&grp->list);
61		free(grp);
62	}
63
64}
65
66static int family_clone(struct nl_object *_dst, struct nl_object *_src)
67{
68	struct genl_family *dst = nl_object_priv(_dst);
69	struct genl_family *src = nl_object_priv(_src);
70	struct genl_family_op *ops;
71	struct genl_family_grp *grp;
72	int err;
73
74	nl_list_for_each_entry(ops, &src->gf_ops, o_list) {
75		err = genl_family_add_op(dst, ops->o_id, ops->o_flags);
76		if (err < 0)
77			return err;
78	}
79
80	nl_list_for_each_entry(grp, &src->gf_mc_grps, list) {
81		err = genl_family_add_grp(dst, grp->id, grp->name);
82		if (err < 0)
83			return err;
84	}
85
86
87	return 0;
88}
89
90static void family_dump_line(struct nl_object *obj, struct nl_dump_params *p)
91{
92	struct genl_family *family = (struct genl_family *) obj;
93
94	nl_dump(p, "0x%04x %s version %u\n",
95		family->gf_id, family->gf_name, family->gf_version);
96}
97
98static const struct trans_tbl ops_flags[] = {
99	__ADD(GENL_ADMIN_PERM, admin_perm)
100	__ADD(GENL_CMD_CAP_DO, has_doit)
101	__ADD(GENL_CMD_CAP_DUMP, has_dump)
102	__ADD(GENL_CMD_CAP_HASPOL, has_policy)
103};
104
105static char *ops_flags2str(int flags, char *buf, size_t len)
106{
107	return __flags2str(flags, buf, len, ops_flags, ARRAY_SIZE(ops_flags));
108}
109
110static void family_dump_details(struct nl_object *obj, struct nl_dump_params *p)
111{
112	struct genl_family_grp *grp;
113	struct genl_family *family = (struct genl_family *) obj;
114
115	family_dump_line(obj, p);
116	nl_dump_line(p, "    hdrsize %u maxattr %u\n",
117		     family->gf_hdrsize, family->gf_maxattr);
118
119	if (family->ce_mask & FAMILY_ATTR_OPS) {
120		struct genl_family_op *op;
121		char buf[64];
122
123		nl_list_for_each_entry(op, &family->gf_ops, o_list) {
124			ops_flags2str(op->o_flags, buf, sizeof(buf));
125
126			genl_op2name(family->gf_id, op->o_id, buf, sizeof(buf));
127
128			nl_dump_line(p, "      op %s (0x%02x)", buf, op->o_id);
129
130			if (op->o_flags)
131				nl_dump(p, " <%s>",
132					ops_flags2str(op->o_flags, buf,
133						      sizeof(buf)));
134
135			nl_dump(p, "\n");
136		}
137	}
138
139	nl_list_for_each_entry(grp, &family->gf_mc_grps, list) {
140		nl_dump_line(p, "      grp %s (0x%02x)\n", grp->name, grp->id);
141	}
142
143}
144
145static void family_dump_stats(struct nl_object *obj, struct nl_dump_params *p)
146{
147	family_dump_details(obj, p);
148}
149
150static int family_compare(struct nl_object *_a, struct nl_object *_b,
151			  uint32_t attrs, int flags)
152{
153	struct genl_family *a = (struct genl_family *) _a;
154	struct genl_family *b = (struct genl_family *) _b;
155	int diff = 0;
156
157#define FAM_DIFF(ATTR, EXPR) ATTR_DIFF(attrs, FAMILY_ATTR_##ATTR, a, b, EXPR)
158
159	diff |= FAM_DIFF(ID,		a->gf_id != b->gf_id);
160	diff |= FAM_DIFF(VERSION,	a->gf_version != b->gf_version);
161	diff |= FAM_DIFF(HDRSIZE,	a->gf_hdrsize != b->gf_hdrsize);
162	diff |= FAM_DIFF(MAXATTR,	a->gf_maxattr != b->gf_maxattr);
163	diff |= FAM_DIFF(NAME,		strcmp(a->gf_name, b->gf_name));
164
165#undef FAM_DIFF
166
167	return diff;
168}
169/** @endcond */
170
171/**
172 * @name Object Allocation
173 * @{
174 */
175
176/**
177 * Allocate new Generic Netlink family object
178 *
179 * @return Newly allocated Generic Netlink family object or NULL.
180 */
181struct genl_family *genl_family_alloc(void)
182{
183	return (struct genl_family *) nl_object_alloc(&genl_family_ops);
184}
185
186/**
187 * Release reference on Generic Netlink family object
188 * @arg family		Generic Netlink family object
189 *
190 * Reduces the reference counter of a Generic Netlink family object by one.
191 * The object is freed after the last user has returned its reference.
192 *
193 * @see nl_object_put()
194 */
195void genl_family_put(struct genl_family *family)
196{
197	nl_object_put((struct nl_object *) family);
198}
199
200/** @} */
201
202/**
203 * @name Numeric Identifier
204 * @{
205 */
206
207/**
208 * Return numeric identifier
209 * @arg family		Generic Netlink family object
210 *
211 * @return Numeric identifier or 0 if not available.
212 */
213unsigned int genl_family_get_id(struct genl_family *family)
214{
215	if (family->ce_mask & FAMILY_ATTR_ID)
216		return family->gf_id;
217	else
218		return GENL_ID_GENERATE;
219}
220
221/**
222 * Set the numeric identifier
223 * @arg family		Generic Netlink family object
224 * @arg id		New numeric identifier
225 */
226void genl_family_set_id(struct genl_family *family, unsigned int id)
227{
228	family->gf_id = id;
229	family->ce_mask |= FAMILY_ATTR_ID;
230}
231
232/** @} */
233
234/**
235 * @name Human Readable Name
236 * @{
237 */
238
239/**
240 * Return human readable name
241 * @arg family		Generic Netlink family object
242 *
243 * @return Name of family or NULL if not available
244 */
245char *genl_family_get_name(struct genl_family *family)
246{
247	if (family->ce_mask & FAMILY_ATTR_NAME)
248		return family->gf_name;
249	else
250		return NULL;
251}
252
253/**
254 * Set human readable name
255 * @arg family		Generic Netlink family object
256 * @arg name		New human readable name
257 */
258void genl_family_set_name(struct genl_family *family, const char *name)
259{
260	strncpy(family->gf_name, name, GENL_NAMSIZ-1);
261	family->ce_mask |= FAMILY_ATTR_NAME;
262}
263
264/**
265 * @name Interface Version
266 * @{
267 */
268
269/**
270 * Return interface version
271 * @arg family		Generic Netlink family object
272 *
273 * @return Interface version or 0 if not available.
274 */
275uint8_t genl_family_get_version(struct genl_family *family)
276{
277	if (family->ce_mask & FAMILY_ATTR_VERSION)
278		return family->gf_version;
279	else
280		return 0;
281}
282
283/**
284 * Set interface version
285 * @arg family		Generic Netlink family object
286 * @arg version		New interface version
287 */
288void genl_family_set_version(struct genl_family *family, uint8_t version)
289{
290	family->gf_version = version;
291	family->ce_mask |= FAMILY_ATTR_VERSION;
292}
293
294/** @} */
295
296/**
297 * @name Header Size
298 * @{
299 */
300
301/**
302 * Return user header size expected by kernel component
303 * @arg family		Generic Netlink family object
304 *
305 * @return Expected header length or 0 if not available.
306 */
307uint32_t genl_family_get_hdrsize(struct genl_family *family)
308{
309	if (family->ce_mask & FAMILY_ATTR_HDRSIZE)
310		return family->gf_hdrsize;
311	else
312		return 0;
313}
314
315void genl_family_set_hdrsize(struct genl_family *family, uint32_t hdrsize)
316{
317	family->gf_hdrsize = hdrsize;
318	family->ce_mask |= FAMILY_ATTR_HDRSIZE;
319}
320
321/** @} */
322
323/**
324 * @name Maximum Expected Attribute
325 * @{
326 */
327
328uint32_t genl_family_get_maxattr(struct genl_family *family)
329{
330	if (family->ce_mask & FAMILY_ATTR_MAXATTR)
331		return family->gf_maxattr;
332	else
333		return family->gf_maxattr;
334}
335
336void genl_family_set_maxattr(struct genl_family *family, uint32_t maxattr)
337{
338	family->gf_maxattr = maxattr;
339	family->ce_mask |= FAMILY_ATTR_MAXATTR;
340}
341
342/** @} */
343
344/**
345 * @name Operations
346 * @{
347 */
348
349int genl_family_add_op(struct genl_family *family, int id, int flags)
350{
351	struct genl_family_op *op;
352
353	op = calloc(1, sizeof(*op));
354	if (op == NULL)
355		return -NLE_NOMEM;
356
357	op->o_id = id;
358	op->o_flags = flags;
359
360	nl_list_add_tail(&op->o_list, &family->gf_ops);
361	family->ce_mask |= FAMILY_ATTR_OPS;
362
363	return 0;
364}
365
366int genl_family_add_grp(struct genl_family *family, uint32_t id,
367	       		const char *name)
368{
369	struct genl_family_grp *grp;
370
371	grp = calloc(1, sizeof(*grp));
372	if (grp == NULL)
373		return -NLE_NOMEM;
374
375	grp->id = id;
376	strncpy(grp->name, name, GENL_NAMSIZ - 1);
377
378	nl_list_add_tail(&grp->list, &family->gf_mc_grps);
379
380	return 0;
381}
382
383/** @} */
384
385/** @cond SKIP */
386struct nl_object_ops genl_family_ops = {
387	.oo_name		= "genl/family",
388	.oo_size		= sizeof(struct genl_family),
389	.oo_constructor		= family_constructor,
390	.oo_free_data		= family_free_data,
391	.oo_clone		= family_clone,
392	.oo_dump = {
393	    [NL_DUMP_LINE]	= family_dump_line,
394	    [NL_DUMP_DETAILS]	= family_dump_details,
395	    [NL_DUMP_STATS]	= family_dump_stats,
396	},
397	.oo_compare		= family_compare,
398	.oo_id_attrs		= FAMILY_ATTR_ID,
399};
400/** @endcond */
401
402/** @} */
403