summaryrefslogtreecommitdiffstats
path: root/src/socket.c
blob: 31d6fbe8bc64fb6f79be5051e6df74663e32a753 (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
/*
 * (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 <libmnl/libmnl.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <unistd.h>
#include <time.h>
#include <errno.h>
#include "internal.h"

/**
 * \mainpage
 *
 * libmnl is a minimalistic user-space library oriented to Netlink developers.
 * There are a lot of common tasks in parsing, validating, constructing of
 * both the Netlink header and TLVs that are repetitive and easy to get wrong.
 * This library aims to provide simple helpers that allows you to avoid
 * re-inventing the wheel in common Netlink tasks.
 *
 * \verbatim
"Simplify, simplify" -- Henry David Thoureau. Walden (1854)
\endverbatim
 *
 * The acronym libmnl stands for LIBrary Minimalistic NetLink.
 *
 * libmnl homepage is:
 *      http://www.netfilter.org/projects/libmnl/
 *
 * \section features Main Features
 * - Small: the shared library requires around 30KB for an x86-based computer.
 * - Simple: this library avoids complex abstractions that tend to hide Netlink
 *   details. It avoids elaborated object-oriented infrastructure and complex
 *   callback-based workflow.
 * - Easy to use: the library simplifies the work for Netlink-wise developers.
 *   It provides functions to make socket handling, message building,
 *   validating, parsing and sequence tracking, easier.
 * - Easy to re-use: you can use this library to build your own abstraction
 *   layer upon this library, if you want to provide another library that
 *   hides Netlink details to your users.
 * - Decoupling: the interdependency of the main bricks that compose this
 *   library is reduced, i.e. the library provides many helpers, but the
 *   programmer is not forced to use them.
 *
 * \section licensing Licensing terms
 * This library is released under the LGPLv2.1 or any later (at your option).
 *
 * \section Dependencies
 * You have to install the Linux kernel headers that you want to use to develop
 * your application. Moreover, this library requires that you have some basics
 * on Netlink.
 *
 * \section scm Git Tree
 * The current development version of libmnl can be accessed at:
 * http://git.netfilter.org/cgi-bin/gitweb.cgi?p=libmnl.git;a=summary
 *
 * \section using Using libmnl
 * You can access several example files under examples/ in the libmnl source
 * code tree.
 */

struct mnl_socket {
	int 			fd;
	struct sockaddr_nl	addr;
};

/**
 * \defgroup socket Netlink socket helpers
 * @{
 */

/**
 * mnl_socket_get_fd - obtain file descriptor from netlink socket
 * \param nl netlink socket obtained via mnl_socket_open()
 *
 * This function returns the file descriptor of a given netlink socket.
 */
EXPORT_SYMBOL(mnl_socket_get_fd);
int mnl_socket_get_fd(const struct mnl_socket *nl)
{
	return nl->fd;
}

/**
 * mnl_socket_get_portid - obtain Netlink PortID from netlink socket
 * \param nl netlink socket obtained via mnl_socket_open()
 *
 * This function returns the Netlink PortID of a given netlink socket.
 * It's a common mistake to assume that this PortID equals the process ID
 * which is not always true. This is the case if you open more than one
 * socket that is binded to the same Netlink subsystem from the same process.
 */
EXPORT_SYMBOL(mnl_socket_get_portid);
unsigned int mnl_socket_get_portid(const struct mnl_socket *nl)
{
	return nl->addr.nl_pid;
}

static struct mnl_socket *__mnl_socket_open(int bus, int flags)
{
	struct mnl_socket *nl;

	nl = calloc(1, sizeof(struct mnl_socket));
	if (nl == NULL)
		return NULL;

	nl->fd = socket(AF_NETLINK, SOCK_RAW | flags, bus);
	if (nl->fd == -1) {
		free(nl);
		return NULL;
	}

	return nl;
}

/**
 * mnl_socket_open - open a netlink socket
 * \param bus the netlink socket bus ID (see NETLINK_* constants)
 *
 * On error, it returns NULL and errno is appropriately set. Otherwise, it
 * returns a valid pointer to the mnl_socket structure.
 */
EXPORT_SYMBOL(mnl_socket_open);
struct mnl_socket *mnl_socket_open(int bus)
{
	return __mnl_socket_open(bus, 0);
}

/**
 * mnl_socket_open2 - open a netlink socket with appropriate flags
 * \param bus the netlink socket bus ID (see NETLINK_* constants)
 * \param flags the netlink socket flags (see SOCK_* constants in socket(2))
 *
 * This is similar to mnl_socket_open(), but allows to set flags like
 * SOCK_CLOEXEC at socket creation time (useful for multi-threaded programs
 * performing exec calls).
 *
 * On error, it returns NULL and errno is appropriately set. Otherwise, it
 * returns a valid pointer to the mnl_socket structure.
 */
EXPORT_SYMBOL(mnl_socket_open2);
struct mnl_socket *mnl_socket_open2(int bus, int flags)
{
	return __mnl_socket_open(bus, flags);
}

/**
 * mnl_socket_fdopen - associates a mnl_socket object with pre-existing socket.
 * \param fd pre-existing socket descriptor.
 *
 * On error, it returns NULL and errno is appropriately set. Otherwise, it
 * returns a valid pointer to the mnl_socket structure. It also sets the portID
 * if the socket fd is already bound and it is AF_NETLINK.
 *
 * Note that mnl_socket_get_portid() returns 0 if this function is used with
 * non-netlink socket.
 */
EXPORT_SYMBOL(mnl_socket_fdopen);
struct mnl_socket *mnl_socket_fdopen(int fd)
{
	int ret;
	struct mnl_socket *nl;
	struct sockaddr_nl addr;
	socklen_t addr_len = sizeof(struct sockaddr_nl);

	ret = getsockname(fd, (struct sockaddr *) &addr, &addr_len);
	if (ret == -1)
		return NULL;

	nl = calloc(1, sizeof(struct mnl_socket));
	if (nl == NULL)
		return NULL;

	nl->fd = fd;
	if (addr.nl_family == AF_NETLINK)
		nl->addr = addr;

	return nl;
}

/**
 * mnl_socket_bind - bind netlink socket
 * \param nl netlink socket obtained via mnl_socket_open()
 * \param groups the group of message you're interested in
 * \param pid the port ID you want to use (use zero for automatic selection)
 *
 * On error, this function returns -1 and errno is appropriately set. On
 * success, 0 is returned. You can use MNL_SOCKET_AUTOPID which is 0 for
 * automatic port ID selection.
 */
EXPORT_SYMBOL(mnl_socket_bind);
int mnl_socket_bind(struct mnl_socket *nl, unsigned int groups, pid_t pid)
{
	int ret;
	socklen_t addr_len;

	nl->addr.nl_family = AF_NETLINK;
	nl->addr.nl_groups = groups;
	nl->addr.nl_pid = pid;

	ret = bind(nl->fd, (struct sockaddr *) &nl->addr, sizeof (nl->addr));
	if (ret < 0)
		return ret;

	addr_len = sizeof(nl->addr);
	ret = getsockname(nl->fd, (struct sockaddr *) &nl->addr, &addr_len);
	if (ret < 0)	
		return ret;

	if (addr_len != sizeof(nl->addr)) {
		errno = EINVAL;
		return -1;
	}
	if (nl->addr.nl_family != AF_NETLINK) {
		errno = EINVAL;
		return -1;
	}
	return 0;
}

/**
 * mnl_socket_sendto - send a netlink message of a certain size
 * \param nl netlink socket obtained via mnl_socket_open()
 * \param buf buffer containing the netlink message to be sent
 * \param len number of bytes in the buffer that you want to send
 *
 * On error, it returns -1 and errno is appropriately set. Otherwise, it 
 * returns the number of bytes sent.
 */
EXPORT_SYMBOL(mnl_socket_sendto);
ssize_t mnl_socket_sendto(const struct mnl_socket *nl, const void *buf,
			  size_t len)
{
	static const struct sockaddr_nl snl = {
		.nl_family = AF_NETLINK
	};
	return sendto(nl->fd, buf, len, 0, 
		      (struct sockaddr *) &snl, sizeof(snl));
}

/**
 * mnl_socket_recvfrom - receive a netlink message
 * \param nl netlink socket obtained via mnl_socket_open()
 * \param buf buffer that you want to use to store the netlink message
 * \param bufsiz size of the buffer passed to store the netlink message
 *
 * On error, it returns -1 and errno is appropriately set. If errno is set
 * to ENOSPC, it means that the buffer that you have passed to store the
 * netlink message is too small, so you have received a truncated message.
 * To avoid this, you have to allocate a buffer of MNL_SOCKET_BUFFER_SIZE
 * (which is 8KB, see linux/netlink.h for more information). Using this
 * buffer size ensures that your buffer is big enough to store the netlink
 * message without truncating it.
 */
EXPORT_SYMBOL(mnl_socket_recvfrom);
ssize_t mnl_socket_recvfrom(const struct mnl_socket *nl, void *buf,
			    size_t bufsiz)
{
	ssize_t ret;
	struct sockaddr_nl addr;
	struct iovec iov = {
		.iov_base	= buf,
		.iov_len	= bufsiz,
	};
	struct msghdr msg = {
		.msg_name	= &addr,
		.msg_namelen	= sizeof(struct sockaddr_nl),
		.msg_iov	= &iov,
		.msg_iovlen	= 1,
		.msg_control	= NULL,
		.msg_controllen	= 0,
		.msg_flags	= 0,
	};
	ret = recvmsg(nl->fd, &msg, 0);
	if (ret == -1)
		return ret;

	if (msg.msg_flags & MSG_TRUNC) {
		errno = ENOSPC;
		return -1;
	}
	if (msg.msg_namelen != sizeof(struct sockaddr_nl)) {
		errno = EINVAL;
		return -1;
	}
	return ret;
}

/**
 * mnl_socket_close - close a given netlink socket
 * \param nl netlink socket obtained via mnl_socket_open()
 *
 * On error, this function returns -1 and errno is appropriately set.
 * On success, it returns 0.
 */
EXPORT_SYMBOL(mnl_socket_close);
int mnl_socket_close(struct mnl_socket *nl)
{
	int ret = close(nl->fd);
	free(nl);
	return ret;
}

/**
 * mnl_socket_setsockopt - set Netlink socket option
 * \param nl netlink socket obtained via mnl_socket_open()
 * \param type type of Netlink socket options
 * \param buf the buffer that contains the data about this option
 * \param len the size of the buffer passed
 *
 * This function allows you to set some Netlink socket option. As of writing
 * this (see linux/netlink.h), the existing options are:
 *
 *	- \#define NETLINK_ADD_MEMBERSHIP  1
 *	- \#define NETLINK_DROP_MEMBERSHIP 2
 *	- \#define NETLINK_PKTINFO         3
 *	- \#define NETLINK_BROADCAST_ERROR 4
 *	- \#define NETLINK_NO_ENOBUFS      5
 *
 * In the early days, Netlink only supported 32 groups expressed in a
 * 32-bits mask. However, since 2.6.14, Netlink may have up to 2^32 multicast
 * groups but you have to use setsockopt() with NETLINK_ADD_MEMBERSHIP to
 * join a given multicast group. This function internally calls setsockopt()
 * to join a given netlink multicast group. You can still use mnl_bind()
 * and the 32-bit mask to join a set of Netlink multicast groups.
 *
 * On error, this function returns -1 and errno is appropriately set.
 */
EXPORT_SYMBOL(mnl_socket_setsockopt);
int mnl_socket_setsockopt(const struct mnl_socket *nl, int type,
			  void *buf, socklen_t len)
{
	return setsockopt(nl->fd, SOL_NETLINK, type, buf, len);
}

/**
 * mnl_socket_getsockopt - get a Netlink socket option
 * \param nl netlink socket obtained via mnl_socket_open()
 * \param type type of Netlink socket options
 * \param buf pointer to the buffer to store the value of this option
 * \param len size of the information written in the buffer
 *
 * On error, this function returns -1 and errno is appropriately set.
 */
EXPORT_SYMBOL(mnl_socket_getsockopt);
int mnl_socket_getsockopt(const struct mnl_socket *nl, int type,
			  void *buf, socklen_t *len)
{
	return getsockopt(nl->fd, SOL_NETLINK, type, buf, len);
}

/**
 * @}
 */