summaryrefslogtreecommitdiffstats
path: root/src/nlmsg.c
blob: f9448a5186961ff900172315b4dfb15cc1e2f69e (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
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
/*
 * (C) 2008-2010 by Pablo Neira Ayuso <pablo@netfilter.org>
 *
 * This program 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; either version 2.1 of the License, or
 * (at your option) any later version.
 */
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <errno.h>
#include <string.h>
#include <libmnl/libmnl.h>
#include "internal.h"

/**
 * \defgroup nlmsg Netlink message helpers
 *
 * Netlink message:
 * \verbatim
	|<----------------- 4 bytes ------------------->|
	|<----- 2 bytes ------>|<------- 2 bytes ------>|
	|-----------------------------------------------|
	|      Message length (including header)        |
	|-----------------------------------------------|
	|     Message type     |     Message flags      |
	|-----------------------------------------------|
	|           Message sequence number             |
	|-----------------------------------------------|
	|                 Netlink PortID                |
	|-----------------------------------------------|
	|                                               |
	.                   Payload                     .
	|_______________________________________________|
\endverbatim
 *
 * There is usually an extra header after the the Netlink header (at the
 * beginning of the payload). This extra header is specific of the Netlink
 * subsystem. After this extra header, it comes the sequence of attributes
 * that are expressed in Type-Length-Value (TLV) format.
 *
 * @{
 */

/**
 * mnl_nlmsg_size - calculate the size of Netlink message (without alignment)
 * \param len length of the Netlink payload
 *
 * This function returns the size of a netlink message (header plus payload)
 * without alignment.
 */
EXPORT_SYMBOL(mnl_nlmsg_size);
size_t mnl_nlmsg_size(size_t len)
{
	return len + MNL_NLMSG_HDRLEN;
}

/**
 * mnl_nlmsg_get_payload_len - get the length of the Netlink payload
 * \param nlh pointer to the header of the Netlink message
 *
 * This function returns the Length of the netlink payload, ie. the length
 * of the full message minus the size of the Netlink header.
 */
EXPORT_SYMBOL(mnl_nlmsg_get_payload_len);
size_t mnl_nlmsg_get_payload_len(const struct nlmsghdr *nlh)
{
	return nlh->nlmsg_len - MNL_NLMSG_HDRLEN;
}

/**
 * mnl_nlmsg_put_header - reserve and prepare room for Netlink header
 * \param buf memory already allocated to store the Netlink header
 *
 * This function sets to zero the room that is required to put the Netlink
 * header in the memory buffer passed as parameter. This function also
 * initializes the nlmsg_len field to the size of the Netlink header. This
 * function returns a pointer to the Netlink header structure.
 */
EXPORT_SYMBOL(mnl_nlmsg_put_header);
struct nlmsghdr *mnl_nlmsg_put_header(void *buf)
{
	int len = MNL_ALIGN(sizeof(struct nlmsghdr));
	struct nlmsghdr *nlh = buf;

	memset(buf, 0, len);
	nlh->nlmsg_len = len;
	return nlh;
}

/**
 * mnl_nlmsg_put_extra_header - reserve and prepare room for an extra header
 * \param nlh pointer to Netlink header
 * \param size size of the extra header that we want to put
 *
 * This function sets to zero the room that is required to put the extra
 * header after the initial Netlink header. This function also increases
 * the nlmsg_len field. You have to invoke mnl_nlmsg_put_header() before
 * you call this function. This function returns a pointer to the extra
 * header.
 */
EXPORT_SYMBOL(mnl_nlmsg_put_extra_header);
void *mnl_nlmsg_put_extra_header(struct nlmsghdr *nlh, size_t size)
{
	char *ptr = (char *)nlh + nlh->nlmsg_len;
	size_t len = MNL_ALIGN(size);
	nlh->nlmsg_len += len;
	memset(ptr, 0, len);
	return ptr;
}

/**
 * mnl_nlmsg_get_payload - get a pointer to the payload of the netlink message
 * \param nlh pointer to a netlink header
 *
 * This function returns a pointer to the payload of the netlink message.
 */
EXPORT_SYMBOL(mnl_nlmsg_get_payload);
void *mnl_nlmsg_get_payload(const struct nlmsghdr *nlh)
{
	return (void *)nlh + MNL_NLMSG_HDRLEN;
}

/**
 * mnl_nlmsg_get_payload_offset - get a pointer to the payload of the message
 * \param nlh pointer to a netlink header
 * \param offset offset to the payload of the attributes TLV set
 *
 * This function returns a pointer to the payload of the netlink message plus
 * a given offset.
 */
EXPORT_SYMBOL(mnl_nlmsg_get_payload_offset);
void *mnl_nlmsg_get_payload_offset(const struct nlmsghdr *nlh, size_t offset)
{
	return (void *)nlh + MNL_NLMSG_HDRLEN + MNL_ALIGN(offset);
}

/**
 * mnl_nlmsg_ok - check a there is room for netlink message
 * \param nlh netlink message that we want to check
 * \param len remaining bytes in a buffer that contains the netlink message
 *
 * This function is used to check that a buffer that contains a netlink
 * message has enough room for the netlink message that it stores, ie. this
 * function can be used to verify that a netlink message is not malformed nor
 * truncated.
 *
 * This function does not set errno in case of error since it is intended
 * for iterations. Thus, it returns true on success and false on error.
 *
 * The len parameter may become negative in malformed messages during message
 * iteration, that is why we use a signed integer.
 */
EXPORT_SYMBOL(mnl_nlmsg_ok);
bool mnl_nlmsg_ok(const struct nlmsghdr *nlh, int len)
{
	return len >= (int)sizeof(struct nlmsghdr) &&
	       nlh->nlmsg_len >= sizeof(struct nlmsghdr) &&
	       (int)nlh->nlmsg_len <= len;
}

/**
 * mnl_nlmsg_next - get the next netlink message in a multipart message
 * \param nlh current netlink message that we are handling
 * \param len length of the remaining bytes in the buffer (passed by reference).
 *
 * This function returns a pointer to the next netlink message that is part
 * of a multi-part netlink message. Netlink can batch several messages into
 * one buffer so that the receiver has to iterate over the whole set of
 * Netlink messages.
 *
 * You have to use mnl_nlmsg_ok() to check if the next Netlink message is
 * valid.
 */
EXPORT_SYMBOL(mnl_nlmsg_next);
struct nlmsghdr *mnl_nlmsg_next(const struct nlmsghdr *nlh, int *len)
{
	*len -= MNL_ALIGN(nlh->nlmsg_len);
	return (struct nlmsghdr *)((void *)nlh + MNL_ALIGN(nlh->nlmsg_len));
}

/**
 * mnl_nlmsg_get_payload_tail - get the ending of the netlink message
 * \param nlh pointer to netlink message
 *
 * This function returns a pointer to the netlink message tail. This is useful
 * to build a message since we continue adding attributes at the end of the
 * message.
 */
EXPORT_SYMBOL(mnl_nlmsg_get_payload_tail);
void *mnl_nlmsg_get_payload_tail(const struct nlmsghdr *nlh)
{
	return (void *)nlh + MNL_ALIGN(nlh->nlmsg_len);
}

/**
 * mnl_nlmsg_seq_ok - perform sequence tracking
 * \param nlh current netlink message that we are handling
 * \param seq last sequence number used to send a message
 *
 * This functions returns true if the sequence tracking is fulfilled, otherwise
 * false is returned. We skip the tracking for netlink messages whose sequence
 * number is zero since it is usually reserved for event-based kernel
 * notifications. On the other hand, if seq is set but the message sequence
 * number is not set (i.e. this is an event message coming from kernel-space),
 * then we also skip the tracking. This approach is good if we use the same
 * socket to send commands to kernel-space (that we want to track) and to
 * listen to events (that we do not track).
 */
EXPORT_SYMBOL(mnl_nlmsg_seq_ok);
bool mnl_nlmsg_seq_ok(const struct nlmsghdr *nlh, unsigned int seq)
{
	return nlh->nlmsg_seq && seq ? nlh->nlmsg_seq == seq : true;
}

/**
 * mnl_nlmsg_portid_ok - perform portID origin check
 * \param nlh current netlink message that we are handling
 * \param portid netlink portid that we want to check
 *
 * This functions returns true if the origin is fulfilled, otherwise
 * false is returned. We skip the tracking for netlink message whose portID
 * is zero since it is reserved for event-based kernel notifications. On the
 * other hand, if portid is set but the message PortID is not (i.e. this
 * is an event message coming from kernel-space), then we also skip the
 * tracking. This approach is good if we use the same socket to send commands
 * to kernel-space (that we want to track) and to listen to events (that we
 * do not track).
 */
EXPORT_SYMBOL(mnl_nlmsg_portid_ok);
bool mnl_nlmsg_portid_ok(const struct nlmsghdr *nlh, unsigned int portid)
{
	return nlh->nlmsg_pid && portid ? nlh->nlmsg_pid == portid : true;
}

static void mnl_nlmsg_fprintf_header(FILE *fd, const struct nlmsghdr *nlh)
{
	fprintf(fd, "----------------\t------------------\n");
	fprintf(fd, "|  %.010u  |\t| message length |\n", nlh->nlmsg_len);
	fprintf(fd, "| %.05u | %c%c%c%c |\t|  type | flags  |\n",
		nlh->nlmsg_type,
		nlh->nlmsg_flags & NLM_F_REQUEST ? 'R' : '-',
		nlh->nlmsg_flags & NLM_F_MULTI ? 'M' : '-',
		nlh->nlmsg_flags & NLM_F_ACK ? 'A' : '-',
		nlh->nlmsg_flags & NLM_F_ECHO ? 'E' : '-');
	fprintf(fd, "|  %.010u  |\t| sequence number|\n", nlh->nlmsg_seq);
	fprintf(fd, "|  %.010u  |\t|     port ID    |\n", nlh->nlmsg_pid);
	fprintf(fd, "----------------\t------------------\n");
}

static void mnl_nlmsg_fprintf_payload(FILE *fd, const struct nlmsghdr *nlh,
				      size_t extra_header_size)
{
	int rem = 0;
	unsigned int i;

	for (i=sizeof(struct nlmsghdr); i<nlh->nlmsg_len; i+=4) {
		char *b = (char *) nlh;
		struct nlattr *attr = (struct nlattr *) (b+i);

		/* netlink control message. */
		if (nlh->nlmsg_type < NLMSG_MIN_TYPE) {
			fprintf(fd, "| %.2x %.2x %.2x %.2x  |\t",
				0xff & b[i],	0xff & b[i+1],
				0xff & b[i+2],	0xff & b[i+3]);
			fprintf(fd, "|                |\n");
		/* special handling for the extra header. */
		} else if (extra_header_size > 0) {
			extra_header_size -= 4;
			fprintf(fd, "| %.2x %.2x %.2x %.2x  |\t",
				0xff & b[i],	0xff & b[i+1],
				0xff & b[i+2],	0xff & b[i+3]);
			fprintf(fd, "|  extra header  |\n");
		/* this seems like an attribute header. */
		} else if (rem == 0 && (attr->nla_type & NLA_TYPE_MASK) != 0) {
			fprintf(fd, "|%c[%d;%dm"
				    "%.5u"
				    "%c[%dm"
				    "|"
				    "%c[%d;%dm"
				    "%c%c"
				    "%c[%dm"
				    "|"
				    "%c[%d;%dm"
				    "%.5u"
				    "%c[%dm|\t",
				27, 1, 31,
				attr->nla_len,
				27, 0,
				27, 1, 32,
				attr->nla_type & NLA_F_NESTED ? 'N' : '-',
				attr->nla_type &
					NLA_F_NET_BYTEORDER ? 'B' : '-',
				27, 0,
				27, 1, 34,
				attr->nla_type & NLA_TYPE_MASK,
				27, 0);
			fprintf(fd, "|len |flags| type|\n");

			if (!(attr->nla_type & NLA_F_NESTED)) {
				rem = NLA_ALIGN(attr->nla_len) -
					sizeof(struct nlattr);
			}
		/* this is the attribute payload. */
		} else if (rem > 0) {
			rem -= 4;
			fprintf(fd, "| %.2x %.2x %.2x %.2x  |\t",
				0xff & b[i],	0xff & b[i+1],
				0xff & b[i+2],	0xff & b[i+3]);
			fprintf(fd, "|      data      |");
			fprintf(fd, "\t %c %c %c %c\n",
				isprint(b[i]) ? b[i] : ' ',
				isprint(b[i+1]) ? b[i+1] : ' ',
				isprint(b[i+2]) ? b[i+2] : ' ',
				isprint(b[i+3]) ? b[i+3] : ' ');
		}
	}
	fprintf(fd, "----------------\t------------------\n");
}

/**
 * mnl_nlmsg_fprintf - print netlink message to file
 * \param fd pointer to file type
 * \param data pointer to the buffer that contains messages to be printed
 * \param datalen length of data stored in the buffer
 * \param extra_header_size size of the extra header (if any)
 *
 * This function prints the netlink header to a file handle.
 * It may be useful for debugging purposes. One example of the output
 * is the following:
 *
 *\verbatim
----------------        ------------------
|  0000000040  |        | message length |
| 00016 | R-A- |        |  type | flags  |
|  1289148991  |        | sequence number|
|  0000000000  |        |     port ID    |
----------------        ------------------
| 00 00 00 00  |        |  extra header  |
| 00 00 00 00  |        |  extra header  |
| 01 00 00 00  |        |  extra header  |
| 01 00 00 00  |        |  extra header  |
|00008|--|00003|        |len |flags| type|
| 65 74 68 30  |        |      data      |       e t h 0
----------------        ------------------
\endverbatim
 *
 * This example above shows the netlink message that is send to kernel-space
 * to set up the link interface eth0. The netlink and attribute header data
 * are displayed in base 10 whereas the extra header and the attribute payload
 * are expressed in base 16. The possible flags in the netlink header are:
 *
 * - R, that indicates that NLM_F_REQUEST is set.
 * - M, that indicates that NLM_F_MULTI is set.
 * - A, that indicates that NLM_F_ACK is set.
 * - E, that indicates that NLM_F_ECHO is set.
 *
 * The lack of one flag is displayed with '-'. On the other hand, the possible
 * attribute flags available are:
 *
 * - N, that indicates that NLA_F_NESTED is set.
 * - B, that indicates that NLA_F_NET_BYTEORDER is set.
 */
EXPORT_SYMBOL(mnl_nlmsg_fprintf);
void mnl_nlmsg_fprintf(FILE *fd, const void *data, size_t datalen,
		       size_t extra_header_size)
{
	const struct nlmsghdr *nlh = data;
	int len = datalen;

	while (mnl_nlmsg_ok(nlh, len)) {
		mnl_nlmsg_fprintf_header(fd, nlh);
		mnl_nlmsg_fprintf_payload(fd, nlh, extra_header_size);
		nlh = mnl_nlmsg_next(nlh, &len);
	}
}

/**
 * \defgroup batch Netlink message batch helpers
 *
 * This library provides helpers to batch several messages into one single
 * datagram. These helpers do not perform strict memory boundary checkings.
 *
 * The following figure represents a Netlink message batch:
 *
 *   |<-------------- MNL_SOCKET_BUFFER_SIZE ------------->|
 *   |<-------------------- batch ------------------>|     |
 *   |-----------|-----------|-----------|-----------|-----------|
 *   |<- nlmsg ->|<- nlmsg ->|<- nlmsg ->|<- nlmsg ->|<- nlmsg ->|
 *   |-----------|-----------|-----------|-----------|-----------|
 *                                             ^           ^
 *                                             |           |
 *                                        message N   message N+1
 *
 * To start the batch, you have to call mnl_nlmsg_batch_start() and you can
 * use mnl_nlmsg_batch_stop() to release it.
 *
 * You have to invoke mnl_nlmsg_batch_next() to get room for a new message
 * in the batch. If this function returns NULL, it means that the last
 * message that was added (message N+1 in the figure above) does not fit the
 * batch. Thus, you have to send the batch (which includes until message N)
 * and, then, you have to call mnl_nlmsg_batch_reset() to re-initialize
 * the batch (this moves message N+1 to the head of the buffer). For that
 * reason, the buffer that you have to use to store the batch must be double
 * of MNL_SOCKET_BUFFER_SIZE to ensure that the last message (message N+1)
 * that did not fit into the batch is written inside valid memory boundaries.
 *
 * @{
 */

struct mnl_nlmsg_batch {
	/* the buffer that is used to store the batch. */
	void *buf;
	size_t limit;
	size_t buflen;
	/* the current netlink message in the batch. */
	void *cur;
	bool overflow;
};

/**
 * mnl_nlmsg_batch_start - initialize a batch
 * \param buf pointer to the buffer that will store this batch
 * \param limit maximum size of the batch (should be MNL_SOCKET_BUFFER_SIZE).
 *
 * The buffer that you pass must be double of MNL_SOCKET_BUFFER_SIZE. The
 * limit must be half of the buffer size, otherwise expect funny memory
 * corruptions 8-).
 *
 * You can allocate the buffer that you use to store the batch in the stack or
 * the heap, no restrictions in this regard. This function returns NULL on
 * error.
 */
EXPORT_SYMBOL(mnl_nlmsg_batch_start);
struct mnl_nlmsg_batch *mnl_nlmsg_batch_start(void *buf, size_t limit)
{
	struct mnl_nlmsg_batch *b;

	b = malloc(sizeof(struct mnl_nlmsg_batch));
	if (b == NULL)
		return NULL;

	b->buf = buf;
	b->limit = limit;
	b->buflen = 0;
	b->cur = buf;
	b->overflow = false;

	return b;
}

/**
 * mnl_nlmsg_batch_stop - release a batch
 * \param b pointer to batch
 *
 * This function releases the batch allocated by mnl_nlmsg_batch_start().
 */
EXPORT_SYMBOL(mnl_nlmsg_batch_stop);
void mnl_nlmsg_batch_stop(struct mnl_nlmsg_batch *b)
{
	free(b);
}

/**
 * mnl_nlmsg_batch_next - get room for the next message in the batch
 * \param b pointer to batch
 *
 * This function returns false if the last message did not fit into the
 * batch. Otherwise, it prepares the batch to provide room for the new
 * Netlink message in the batch and returns true.
 *
 * You have to put at least one message in the batch before calling this
 * function, otherwise your application is likely to crash.
 */
EXPORT_SYMBOL(mnl_nlmsg_batch_next);
bool mnl_nlmsg_batch_next(struct mnl_nlmsg_batch *b)
{
	struct nlmsghdr *nlh = b->cur;

	if (b->buflen + nlh->nlmsg_len > b->limit) {
		b->overflow = true;
		return false;
	}
	b->cur = b->buf + b->buflen + nlh->nlmsg_len;
	b->buflen += nlh->nlmsg_len;
	return true;
}

/**
 * mnl_nlmsg_batch_reset - reset the batch
 * \param b pointer to batch
 *
 * This function allows to reset a batch, so you can reuse it to create a
 * new one. This function moves the last message which does not fit the
 * batch to the head of the buffer, if any.
 */
EXPORT_SYMBOL(mnl_nlmsg_batch_reset);
void mnl_nlmsg_batch_reset(struct mnl_nlmsg_batch *b)
{
	if (b->overflow) {
		struct nlmsghdr *nlh = b->cur;
		memcpy(b->buf, b->cur, nlh->nlmsg_len);
		b->buflen = nlh->nlmsg_len;
		b->cur = b->buf + b->buflen;
		b->overflow = false;
	} else {
		b->buflen = 0;
		b->cur = b->buf;
	}
}

/**
 * mnl_nlmsg_batch_size - get current size of the batch
 * \param b pointer to batch
 *
 * This function returns the current size of the batch.
 */
EXPORT_SYMBOL(mnl_nlmsg_batch_size);
size_t mnl_nlmsg_batch_size(struct mnl_nlmsg_batch *b)
{
	return b->buflen;
}

/**
 * mnl_nlmsg_batch_head - get head of this batch
 * \param b pointer to batch
 *
 * This function returns a pointer to the head of the batch, which is the
 * beginning of the buffer that is used.
 */
EXPORT_SYMBOL(mnl_nlmsg_batch_head);
void *mnl_nlmsg_batch_head(struct mnl_nlmsg_batch *b)
{
	return b->buf;
}

/**
 * mnl_nlmsg_batch_current - returns current position in the batch
 * \param b pointer to batch
 *
 * This function returns a pointer to the current position in the buffer
 * that is used to store the batch.
 */
EXPORT_SYMBOL(mnl_nlmsg_batch_current);
void *mnl_nlmsg_batch_current(struct mnl_nlmsg_batch *b)
{
	return b->cur;
}

/**
 * mnl_nlmsg_batch_is_empty - check if there is any message in the batch
 * \param b pointer to batch
 *
 * This function returns true if the batch is empty.
 */
EXPORT_SYMBOL(mnl_nlmsg_batch_is_empty);
bool mnl_nlmsg_batch_is_empty(struct mnl_nlmsg_batch *b)
{
	return b->buflen == 0;
}

/**
 * @}
 */