[adm5120] fix support for 2.6.38 kernel
[openwrt.git] / package / libnl-tiny / src / socket.c
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
25 static uint32_t used_ports_map[32];
26
27 static 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
54 static 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
70 static 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 */
97 struct 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 */
117 struct 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 */
129 void 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
151 static 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 */
168 void 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 */
184 void 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 */
221 int 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 */
260 int 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 */
296 int 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 */
327 int 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 */
362 int 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 */
389 int 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 /** @} */
This page took 0.072331 seconds and 5 git commands to generate.