summaryrefslogtreecommitdiff
path: root/lib/genl/genl.c
blob: 4fd796229b1ad2c1028f892a84719718d61456cb (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
/* SPDX-License-Identifier: LGPL-2.1-only */
/*
 * Copyright (c) 2003-2012 Thomas Graf <tgraf@suug.ch>
 */

/**
 * @defgroup genl Generic Netlink Library (libnl-genl)
 *
 * @{
 */

#include <netlink-private/genl.h>
#include <netlink/netlink.h>
#include <netlink/genl/genl.h>
#include <netlink/utils.h>

/**
 * @name Generic Netlink Socket
 * @{
 */

/**
 * Connect a Generic Netlink socket
 * @arg sk		Unconnected Netlink socket
 *
 * This function expects a struct nl_socket object previously allocated via
 * nl_socket_alloc(). It calls nl_connect() to create the local socket file
 * descriptor and binds the socket to the \c NETLINK_GENERIC Netlink protocol.
 *
 * Using this function is equivalent to:
 * @code
 * nl_connect(sk, NETLINK_GENERIC);
 * @endcode
 *
 * @see nl_connect()
 *
 * @return 0 on success or a negative error code.
 */
int genl_connect(struct nl_sock *sk)
{
	return nl_connect(sk, NETLINK_GENERIC);
}

/** @} */

/**
 * @name Sending Data
 * @{
 */

/**
 * Send a Generic Netlink message consisting only of a header
 * @arg sk		Generic Netlink socket
 * @arg family		Numeric family identifier
 * @arg cmd		Numeric command identifier
 * @arg version		Interface version
 * @arg flags		Additional Netlink message flags (optional)
 *
 * This function is a shortcut for sending a Generic Netlink message without
 * any message payload. The message will only consist of the Netlink and
 * Generic Netlink headers. The header is constructed based on the specified
 * parameters and passed on to nl_send_simple() to send it on the specified
 * socket.
 *
 * @par Example:
 * @code
 * #include <netlink/genl/genl.h>
 * #include <linux/genetlink.h>
 *
 * err = genl_send_simple(sk, GENL_ID_CTRL, CTRL_CMD_GETFAMILY, CTRL_VERSION,
 *                        NLM_F_DUMP);
 * @endcode
 *
 * @see nl_send_simple()
 *
 * @return 0 on success or a negative error code. Due to a bug, this function
 * returns the number of bytes sent. Treat any non-negative number as success.
 */
int genl_send_simple(struct nl_sock *sk, int family, int cmd,
		     int version, int flags)
{
	struct genlmsghdr hdr = {
		.cmd = cmd,
		.version = version,
	};

	return nl_send_simple(sk, family, flags, &hdr, sizeof(hdr));
}

/** @} */

/**
 * @name Message Parsing
 * @{
 */

/**
 * Validate Generic Netlink message headers
 * @arg nlh		Pointer to Netlink message header
 * @arg hdrlen		Length of user header
 *
 * Verifies the integrity of the Netlink and Generic Netlink headers by
 * enforcing the following requirements:
 *  - Valid Netlink message header (nlmsg_valid_hdr())
 *  - Presence of a complete Generic Netlink header
 *  - At least \c hdrlen bytes of payload included after the generic
 *    netlink header.
 *
 * @return A positive integer (true) if the headers are valid or
 *         0 (false) if not.
 */
int genlmsg_valid_hdr(struct nlmsghdr *nlh, int hdrlen)
{
	struct genlmsghdr *ghdr;

	if (!nlmsg_valid_hdr(nlh, GENL_HDRLEN))
		return 0;

	ghdr = nlmsg_data(nlh);
	if (genlmsg_len(ghdr) < NLMSG_ALIGN(hdrlen))
		return 0;

	return 1;
}

/**
 * Validate Generic Netlink message including attributes
 * @arg nlh		Pointer to Netlink message header
 * @arg hdrlen		Length of user header
 * @arg maxtype		Maximum attribtue id expected
 * @arg policy		Attribute validation policy
 *
 * Verifies the validity of the Netlink and Generic Netlink headers using
 * genlmsg_valid_hdr() and calls nla_validate() on the message payload to
 * verify the integrity of eventual attributes.
 *
 * @note You may call genlmsg_parse() directly to perform validation and
 *       parsing in a single step. 
 *
 * @see genlmsg_valid_hdr()
 * @see nla_validate()
 * @see genlmsg_parse()
 *
 * @return 0 on success or a negative error code.
 */
int genlmsg_validate(struct nlmsghdr *nlh, int hdrlen, int maxtype,
		     const struct nla_policy *policy)
{
	struct genlmsghdr *ghdr;

	if (!genlmsg_valid_hdr(nlh, hdrlen))
		return -NLE_MSG_TOOSHORT;

	ghdr = nlmsg_data(nlh);
	return nla_validate(genlmsg_attrdata(ghdr, hdrlen),
			    genlmsg_attrlen(ghdr, hdrlen), maxtype, policy);
}

/**
 * Parse Generic Netlink message including attributes
 * @arg nlh		Pointer to Netlink message header
 * @arg hdrlen		Length of user header
 * @arg tb		Array to store parsed attributes
 * @arg maxtype		Maximum attribute id expected
 * @arg policy		Attribute validation policy
 *
 * Verifies the validity of the Netlink and Generic Netlink headers using
 * genlmsg_valid_hdr() and calls nla_parse() on the message payload to
 * parse eventual attributes.
 *
 * @par Example:
 * @code
 * struct nlattr *attrs[MY_TYPE_MAX+1];
 *
 * if ((err = genlmsg_parse(nlmsg_hdr(msg), sizeof(struct my_hdr), attrs,
 *                          MY_TYPE_MAX, attr_policy)) < 0)
 * 	// ERROR
 * @endcode
 *
 * @see genlmsg_valid_hdr()
 * @see genlmsg_validate()
 * @see nla_parse()
 *
 * @return 0 on success or a negative error code.
 */
int genlmsg_parse(struct nlmsghdr *nlh, int hdrlen, struct nlattr *tb[],
		  int maxtype, const struct nla_policy *policy)
{
	struct genlmsghdr *ghdr;

	if (!genlmsg_valid_hdr(nlh, hdrlen))
		return -NLE_MSG_TOOSHORT;

	ghdr = nlmsg_data(nlh);
	return nla_parse(tb, maxtype, genlmsg_attrdata(ghdr, hdrlen),
			 genlmsg_attrlen(ghdr, hdrlen), policy);
}

/**
 * Return pointer to Generic Netlink header
 * @arg nlh		Netlink message header
 *
 * @return Pointer to Generic Netlink message header
 */
struct genlmsghdr *genlmsg_hdr(struct nlmsghdr *nlh)
{
	return nlmsg_data(nlh);
}

/**
 * Return length of message payload including user header
 * @arg gnlh		Generic Netlink message header
 *
 * @see genlmsg_data()
 *
 * @return Length of user payload including an eventual user header in
 *         number of bytes.
 */
int genlmsg_len(const struct genlmsghdr *gnlh)
{
	const struct nlmsghdr *nlh;

	nlh = (const struct nlmsghdr *)((const unsigned char *) gnlh - NLMSG_HDRLEN);
	return (nlh->nlmsg_len - GENL_HDRLEN - NLMSG_HDRLEN);
}


/**
 * Return pointer to user header
 * @arg gnlh		Generic Netlink message header
 *
 * Calculates the pointer to the user header based on the pointer to
 * the Generic Netlink message header.
 *
 * @return Pointer to the user header
 */
void *genlmsg_user_hdr(const struct genlmsghdr *gnlh)
{
	return genlmsg_data(gnlh);
}

/**
 * Return pointer to user data
 * @arg gnlh		Generic netlink message header
 * @arg hdrlen		Length of user header
 *
 * Calculates the pointer to the user data based on the pointer to
 * the Generic Netlink message header.
 *
 * @see genlmsg_user_datalen()
 *
 * @return Pointer to the user data
 */
void *genlmsg_user_data(const struct genlmsghdr *gnlh, const int hdrlen)
{
	return (char *) genlmsg_user_hdr(gnlh) + NLMSG_ALIGN(hdrlen);
}

/**
 * Return length of user data
 * @arg gnlh		Generic Netlink message header
 * @arg hdrlen		Length of user header
 *
 * @see genlmsg_user_data()
 *
 * @return Length of user data in bytes
 */
int genlmsg_user_datalen(const struct genlmsghdr *gnlh, const int hdrlen)
{
	return genlmsg_len(gnlh) - NLMSG_ALIGN(hdrlen);
}

/**
 * Return pointer to message attributes
 * @arg gnlh		Generic Netlink message header
 * @arg hdrlen		Length of user header
 *
 * @see genlmsg_attrlen()
 *
 * @return Pointer to the start of the message's attributes section.
 */
struct nlattr *genlmsg_attrdata(const struct genlmsghdr *gnlh, int hdrlen)
{
	return genlmsg_user_data(gnlh, hdrlen);
}

/**
 * Return length of message attributes
 * @arg gnlh		Generic Netlink message header
 * @arg hdrlen		Length of user header
 *
 * @see genlmsg_attrdata()
 *
 * @return Length of the message section containing attributes in number
 *         of bytes.
 */
int genlmsg_attrlen(const struct genlmsghdr *gnlh, int hdrlen)
{
	return genlmsg_len(gnlh) - NLMSG_ALIGN(hdrlen);
}

/** @} */

/**
 * @name Message Construction
 * @{
 */

/**
 * Add Generic Netlink headers to Netlink message
 * @arg msg		Netlink message object
 * @arg port		Netlink port or NL_AUTO_PORT
 * @arg seq		Sequence number of message or NL_AUTO_SEQ
 * @arg family		Numeric family identifier
 * @arg hdrlen		Length of user header
 * @arg flags		Additional Netlink message flags (optional)
 * @arg cmd		Numeric command identifier
 * @arg version		Interface version
 *
 * Calls nlmsg_put() on the specified message object to reserve space for
 * the Netlink header, the Generic Netlink header, and a user header of
 * specified length. Fills out the header fields with the specified
 * parameters.
 *
 * @par Example:
 * @code
 * struct nl_msg *msg;
 * struct my_hdr *user_hdr;
 *
 * if (!(msg = nlmsg_alloc()))
 * 	// ERROR
 *
 * user_hdr = genlmsg_put(msg, NL_AUTO_PORT, NL_AUTO_SEQ, family_id,
 *                        sizeof(struct my_hdr), 0, MY_CMD_FOO, 0);
 * if (!user_hdr)
 * 	// ERROR
 * @endcode
 *
 * @see nlmsg_put()
 *
 * Returns Pointer to user header or NULL if an error occurred.
 */
void *genlmsg_put(struct nl_msg *msg, uint32_t port, uint32_t seq, int family,
		  int hdrlen, int flags, uint8_t cmd, uint8_t version)
{
	struct nlmsghdr *nlh;
	struct genlmsghdr hdr = {
		.cmd = cmd,
		.version = version,
	};

	nlh = nlmsg_put(msg, port, seq, family, GENL_HDRLEN + hdrlen, flags);
	if (nlh == NULL)
		return NULL;

	memcpy(nlmsg_data(nlh), &hdr, sizeof(hdr));
	NL_DBG(2, "msg %p: Added generic netlink header cmd=%d version=%d\n",
	       msg, cmd, version);

	return (char *) nlmsg_data(nlh) + GENL_HDRLEN;
}

/** @} */

/**
 * @name Deprecated
 * @{
 */

/**
 * Return pointer to message payload
 * @arg gnlh		Generic Netlink message header
 *
 * @deprecated This function has been deprecated due to inability to specify
 *             the length of the user header. Use genlmsg_user_hdr()
 *             respectively genlmsg_user_data().
 *
 * @return Pointer to payload section
 */
void *genlmsg_data(const struct genlmsghdr *gnlh)
{
	return ((unsigned char *) gnlh + GENL_HDRLEN);
}

/** @} */
/** @} */