1/*
2 * lib/socket.c		Netlink Socket
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-2008 Thomas Graf <tgraf@suug.ch>
10 */
11
12/**
13 * @ingroup core
14 * @defgroup socket Socket
15 * @{
16 */
17
18#include <netlink-local.h>
19#include <netlink/netlink.h>
20#include <netlink/utils.h>
21#include <netlink/handlers.h>
22#include <netlink/msg.h>
23#include <netlink/attr.h>
24
25static uint32_t used_ports_map[32];
26
27static uint32_t generate_local_port(void)
28{
29	int i, n;
30	uint32_t pid = getpid() & 0x3FFFFF;
31
32	for (i = 0; i < 32; i++) {
33		if (used_ports_map[i] == 0xFFFFFFFF)
34			continue;
35
36		for (n = 0; n < 32; n++) {
37			if (1UL & (used_ports_map[i] >> n))
38				continue;
39
40			used_ports_map[i] |= (1UL << n);
41			n += (i * 32);
42
43			/* PID_MAX_LIMIT is currently at 2^22, leaving 10 bit
44			 * to, i.e. 1024 unique ports per application. */
45			return pid + (n << 22);
46
47		}
48	}
49
50	/* Out of sockets in our own PID namespace, what to do? FIXME */
51	return UINT_MAX;
52}
53
54static void release_local_port(uint32_t port)
55{
56	int nr;
57
58	if (port == UINT_MAX)
59		return;
60
61	nr = port >> 22;
62	used_ports_map[nr / 32] &= ~(1 << nr % 32);
63}
64
65/**
66 * @name Allocation
67 * @{
68 */
69
70static struct nl_sock *__alloc_socket(struct nl_cb *cb)
71{
72	struct nl_sock *sk;
73
74	sk = calloc(1, sizeof(*sk));
75	if (!sk)
76		return NULL;
77
78	sk->s_fd = -1;
79	sk->s_cb = cb;
80	sk->s_local.nl_family = AF_NETLINK;
81	sk->s_peer.nl_family = AF_NETLINK;
82	sk->s_seq_expect = sk->s_seq_next = time(0);
83	sk->s_local.nl_pid = generate_local_port();
84	if (sk->s_local.nl_pid == UINT_MAX) {
85		nl_socket_free(sk);
86		return NULL;
87	}
88
89	return sk;
90}
91
92/**
93 * Allocate new netlink socket
94 *
95 * @return Newly allocated netlink socket or NULL.
96 */
97struct nl_sock *nl_socket_alloc(void)
98{
99	struct nl_cb *cb;
100
101	cb = nl_cb_alloc(NL_CB_DEFAULT);
102	if (!cb)
103		return NULL;
104
105	return __alloc_socket(cb);
106}
107
108/**
109 * Allocate new socket with custom callbacks
110 * @arg cb		Callback handler
111 *
112 * The reference to the callback handler is taken into account
113 * automatically, it is released again upon calling nl_socket_free().
114 *
115 *@return Newly allocted socket handle or NULL.
116 */
117struct nl_sock *nl_socket_alloc_cb(struct nl_cb *cb)
118{
119	if (cb == NULL)
120		BUG();
121
122	return __alloc_socket(nl_cb_get(cb));
123}
124
125/**
126 * Free a netlink socket.
127 * @arg sk		Netlink socket.
128 */
129void nl_socket_free(struct nl_sock *sk)
130{
131	if (!sk)
132		return;
133
134	if (sk->s_fd >= 0)
135		close(sk->s_fd);
136
137	if (!(sk->s_flags & NL_OWN_PORT))
138		release_local_port(sk->s_local.nl_pid);
139
140	nl_cb_put(sk->s_cb);
141	free(sk);
142}
143
144/** @} */
145
146/**
147 * @name Sequence Numbers
148 * @{
149 */
150
151static int noop_seq_check(struct nl_msg *msg, void *arg)
152{
153	return NL_OK;
154}
155
156
157/**
158 * Disable sequence number checking.
159 * @arg sk		Netlink socket.
160 *
161 * Disables checking of sequence numbers on the netlink socket This is
162 * required to allow messages to be processed which were not requested by
163 * a preceding request message, e.g. netlink events.
164 *
165 * @note This function modifies the NL_CB_SEQ_CHECK configuration in
166 * the callback handle associated with the socket.
167 */
168void nl_socket_disable_seq_check(struct nl_sock *sk)
169{
170	nl_cb_set(sk->s_cb, NL_CB_SEQ_CHECK,
171		  NL_CB_CUSTOM, noop_seq_check, NULL);
172}
173
174/** @} */
175
176/**
177 * Set local port of socket
178 * @arg sk		Netlink socket.
179 * @arg port		Local port identifier
180 *
181 * Assigns a local port identifier to the socket. If port is 0
182 * a unique port identifier will be generated automatically.
183 */
184void nl_socket_set_local_port(struct nl_sock *sk, uint32_t port)
185{
186	if (port == 0) {
187		port = generate_local_port();
188		sk->s_flags &= ~NL_OWN_PORT;
189	} else  {
190		if (!(sk->s_flags & NL_OWN_PORT))
191			release_local_port(sk->s_local.nl_pid);
192		sk->s_flags |= NL_OWN_PORT;
193	}
194
195	sk->s_local.nl_pid = port;
196}
197
198/** @} */
199
200/**
201 * @name Group Subscriptions
202 * @{
203 */
204
205/**
206 * Join groups
207 * @arg sk		Netlink socket
208 * @arg group		Group identifier
209 *
210 * Joins the specified groups using the modern socket option which
211 * is available since kernel version 2.6.14. It allows joining an
212 * almost arbitary number of groups without limitation.  The list
213 * of groups has to be terminated by 0 (%NFNLGRP_NONE).
214 *
215 * Make sure to use the correct group definitions as the older
216 * bitmask definitions for nl_join_groups() are likely to still
217 * be present for backward compatibility reasons.
218 *
219 * @return 0 on sucess or a negative error code.
220 */
221int nl_socket_add_memberships(struct nl_sock *sk, int group, ...)
222{
223	int err;
224	va_list ap;
225
226	if (sk->s_fd == -1)
227		return -NLE_BAD_SOCK;
228
229	va_start(ap, group);
230
231	while (group != 0) {
232		if (group < 0)
233			return -NLE_INVAL;
234
235		err = setsockopt(sk->s_fd, SOL_NETLINK, NETLINK_ADD_MEMBERSHIP,
236						 &group, sizeof(group));
237		if (err < 0)
238			return -nl_syserr2nlerr(errno);
239
240		group = va_arg(ap, int);
241	}
242
243	va_end(ap);
244
245	return 0;
246}
247
248/**
249 * Leave groups
250 * @arg sk		Netlink socket
251 * @arg group		Group identifier
252 *
253 * Leaves the specified groups using the modern socket option
254 * which is available since kernel version 2.6.14. The list of groups
255 * has to terminated by 0 (%NFNLGRP_NONE).
256 *
257 * @see nl_socket_add_membership
258 * @return 0 on success or a negative error code.
259 */
260int nl_socket_drop_memberships(struct nl_sock *sk, int group, ...)
261{
262	int err;
263	va_list ap;
264
265	if (sk->s_fd == -1)
266		return -NLE_BAD_SOCK;
267
268	va_start(ap, group);
269
270	while (group != 0) {
271		if (group < 0)
272			return -NLE_INVAL;
273
274		err = setsockopt(sk->s_fd, SOL_NETLINK, NETLINK_DROP_MEMBERSHIP,
275						 &group, sizeof(group));
276		if (err < 0)
277			return -nl_syserr2nlerr(errno);
278
279		group = va_arg(ap, int);
280	}
281
282	va_end(ap);
283
284	return 0;
285}
286
287
288/** @} */
289
290/**
291 * Set file descriptor of socket to non-blocking state
292 * @arg sk		Netlink socket.
293 *
294 * @return 0 on success or a negative error code.
295 */
296int nl_socket_set_nonblocking(struct nl_sock *sk)
297{
298	if (sk->s_fd == -1)
299		return -NLE_BAD_SOCK;
300
301	if (fcntl(sk->s_fd, F_SETFL, O_NONBLOCK) < 0)
302		return -nl_syserr2nlerr(errno);
303
304	return 0;
305}
306
307/** @} */
308
309/**
310 * @name Utilities
311 * @{
312 */
313
314/**
315 * Set socket buffer size of netlink socket.
316 * @arg sk		Netlink socket.
317 * @arg rxbuf		New receive socket buffer size in bytes.
318 * @arg txbuf		New transmit socket buffer size in bytes.
319 *
320 * Sets the socket buffer size of a netlink socket to the specified
321 * values \c rxbuf and \c txbuf. Providing a value of \c 0 assumes a
322 * good default value.
323 *
324 * @note It is not required to call this function prior to nl_connect().
325 * @return 0 on sucess or a negative error code.
326 */
327int nl_socket_set_buffer_size(struct nl_sock *sk, int rxbuf, int txbuf)
328{
329	int err;
330
331	if (rxbuf <= 0)
332		rxbuf = 32768;
333
334	if (txbuf <= 0)
335		txbuf = 32768;
336
337	if (sk->s_fd == -1)
338		return -NLE_BAD_SOCK;
339
340	err = setsockopt(sk->s_fd, SOL_SOCKET, SO_SNDBUF,
341			 &txbuf, sizeof(txbuf));
342	if (err < 0)
343		return -nl_syserr2nlerr(errno);
344
345	err = setsockopt(sk->s_fd, SOL_SOCKET, SO_RCVBUF,
346			 &rxbuf, sizeof(rxbuf));
347	if (err < 0)
348		return -nl_syserr2nlerr(errno);
349
350	sk->s_flags |= NL_SOCK_BUFSIZE_SET;
351
352	return 0;
353}
354
355/**
356 * Enable/disable credential passing on netlink socket.
357 * @arg sk		Netlink socket.
358 * @arg state		New state (0 - disabled, 1 - enabled)
359 *
360 * @return 0 on success or a negative error code
361 */
362int nl_socket_set_passcred(struct nl_sock *sk, int state)
363{
364	int err;
365
366	if (sk->s_fd == -1)
367		return -NLE_BAD_SOCK;
368
369	err = setsockopt(sk->s_fd, SOL_SOCKET, SO_PASSCRED,
370			 &state, sizeof(state));
371	if (err < 0)
372		return -nl_syserr2nlerr(errno);
373
374	if (state)
375		sk->s_flags |= NL_SOCK_PASSCRED;
376	else
377		sk->s_flags &= ~NL_SOCK_PASSCRED;
378
379	return 0;
380}
381
382/**
383 * Enable/disable receival of additional packet information
384 * @arg sk		Netlink socket.
385 * @arg state		New state (0 - disabled, 1 - enabled)
386 *
387 * @return 0 on success or a negative error code
388 */
389int nl_socket_recv_pktinfo(struct nl_sock *sk, int state)
390{
391	int err;
392
393	if (sk->s_fd == -1)
394		return -NLE_BAD_SOCK;
395
396	err = setsockopt(sk->s_fd, SOL_NETLINK, NETLINK_PKTINFO,
397			 &state, sizeof(state));
398	if (err < 0)
399		return -nl_syserr2nlerr(errno);
400
401	return 0;
402}
403
404/** @} */
405
406/** @} */
407