diff options
Diffstat (limited to 'lib/socket.c')
-rw-r--r-- | lib/socket.c | 405 |
1 files changed, 194 insertions, 211 deletions
diff --git a/lib/socket.c b/lib/socket.c index aae8f54..8083bbb 100644 --- a/lib/socket.c +++ b/lib/socket.c @@ -1,91 +1,17 @@ /* - * lib/socket.c Netlink Socket Handle + * lib/socket.c Netlink Socket * * This library 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 version 2.1 * of the License. * - * Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch> + * Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch> */ /** - * @ingroup nl + * @ingroup core * @defgroup socket Socket - * @brief Handle representing a netlink socket. - * - * The socket is represented in a structure called the netlink handle, - * besides the socket, it stores various settings and values related - * to the socket. Every socket handle has a mandatory association with - * a set of callbacks which can be used to modify the behaviour when - * sending/receiving data from the socket. - * - * @par Socket Attributes - * - \b Local \b Port: The local port is a netlink port identifying the - * local endpoint. It is used as source address for outgoing messages - * and will be addressed in replies. It must therefore be unique among - * all userspace applications. When the socket handle is allocated, a - * unique port number is generated automatically in the form of 22 bits - * Process Identifier + 10 bits Arbitary Number. Therefore the library - * is capable of generating 1024 unique local port numbers for every - * process. If more sockets are required, the application has to manage - * port numbers itself using nl_socket_set_local_port(). - * - \b Group \b Subscriptions: A socket can subscribe to any number of - * multicast groups. It will then receive a copy of all messages sent - * to one of the groups. This method is mainly used for event notification. - * Prior to kernel 2.6.14, the group subscription was done via bitmask - * which limited to a total number of groups of 32. With 2.6.14 a new - * method was added based on continous identifiers which supports an - * arbitary number of groups. Both methods are supported, see - * nl_join_groups() respectively nl_socket_add_membership() and - * nl_socket_drop_membership(). - * - \b Peer \b Port: The peer port is a netlink port identifying the - * peer's endpoint. If no peer port is specified, the kernel will try to - * autobind to a socket of the specified netlink family automatically. - * This is very common as typically only one listening socket exists - * on the kernel side. The peer port can be modified using - * nl_socket_set_peer_port(). - * - \b Peer \b Groups: - * - \b File \b Descriptor: The file descriptor of the socket, it can be - * accessed via nl_socket_get_fd() to change socket options or monitor - * activity using poll()/select(). - * - \b Protocol: Once connected, the socket is bound to stick to one - * netlink family. This field is invisible, it is maintained automatically. - * (See nl_connect()) - * - \b Next \b Sequence \b Number: Next available sequence number to be used - * for the next message being sent out. (Initial value: UNIX time when the - * socket was allocated.) Sequence numbers can be used via - * nl_socket_use_seq(). - * - \b Expected \b Sequence \b Number: Expected sequence number in the next - * message received from the socket. (Initial value: Equal to next sequence - * number.) - * - \b Callbacks \b Configuration: - * - * @par 1) Creating the netlink handle - * @code - * struct nl_handle *handle; - * - * // Allocate and initialize a new netlink handle - * handle = nl_handle_alloc(); - * - * // Use nl_socket_get_fd() to fetch the file description, for example to - * // put a socket into non-blocking i/o mode. - * fcntl(nl_socket_get_fd(handle), F_SETFL, O_NONBLOCK); - * @endcode - * - * @par 2) Group Subscriptions - * @code - * // Event notifications are typically sent to multicast addresses which - * // represented by groups. Join a group to f.e. receive link notifications. - * nl_socket_add_membership(handle, RTNLGRP_LINK); - * @endcode - * - * @par 6) Cleaning up - * @code - * // Finally destroy the netlink handle - * nl_handle_destroy(handle); - * @endcode - * * @{ */ @@ -153,7 +79,7 @@ static void release_local_port(uint32_t port) return; nr = port >> 22; - used_ports_map[nr / 32] &= ~((nr % 32) + 1); + used_ports_map[nr / 32] &= ~(1 << nr % 32); } /** @@ -161,83 +87,78 @@ static void release_local_port(uint32_t port) * @{ */ -static struct nl_handle *__alloc_handle(struct nl_cb *cb) +static struct nl_sock *__alloc_socket(struct nl_cb *cb) { - struct nl_handle *handle; + struct nl_sock *sk; - handle = calloc(1, sizeof(*handle)); - if (!handle) { - nl_errno(ENOMEM); + sk = calloc(1, sizeof(*sk)); + if (!sk) return NULL; - } - handle->h_fd = -1; - handle->h_cb = cb; - handle->h_local.nl_family = AF_NETLINK; - handle->h_peer.nl_family = AF_NETLINK; - handle->h_seq_expect = handle->h_seq_next = time(0); - handle->h_local.nl_pid = generate_local_port(); - if (handle->h_local.nl_pid == UINT_MAX) { - nl_handle_destroy(handle); - nl_error(ENOBUFS, "Out of local ports"); + sk->s_fd = -1; + sk->s_cb = cb; + sk->s_local.nl_family = AF_NETLINK; + sk->s_peer.nl_family = AF_NETLINK; + sk->s_seq_expect = sk->s_seq_next = time(0); + sk->s_local.nl_pid = generate_local_port(); + if (sk->s_local.nl_pid == UINT_MAX) { + nl_socket_free(sk); return NULL; } - return handle; + return sk; } /** - * Allocate new netlink socket handle. + * Allocate new netlink socket * - * @return Newly allocated netlink socket handle or NULL. + * @return Newly allocated netlink socket or NULL. */ -struct nl_handle *nl_handle_alloc(void) +struct nl_sock *nl_socket_alloc(void) { struct nl_cb *cb; cb = nl_cb_alloc(default_cb); - if (!cb) { - nl_errno(ENOMEM); + if (!cb) return NULL; - } - return __alloc_handle(cb); + return __alloc_socket(cb); } /** - * Allocate new socket handle with custom callbacks + * Allocate new socket with custom callbacks * @arg cb Callback handler * * The reference to the callback handler is taken into account - * automatically, it is released again upon calling nl_handle_destroy(). + * automatically, it is released again upon calling nl_socket_free(). * *@return Newly allocted socket handle or NULL. */ -struct nl_handle *nl_handle_alloc_cb(struct nl_cb *cb) +struct nl_sock *nl_socket_alloc_cb(struct nl_cb *cb) { if (cb == NULL) BUG(); - return __alloc_handle(nl_cb_get(cb)); + return __alloc_socket(nl_cb_get(cb)); } /** - * Destroy netlink handle. - * @arg handle Netlink handle. + * Free a netlink socket. + * @arg sk Netlink socket. */ -void nl_handle_destroy(struct nl_handle *handle) +void nl_socket_free(struct nl_sock *sk) { - if (!handle) + if (!sk) return; - if (handle->h_fd >= 0) - close(handle->h_fd); + if (sk->s_fd >= 0) + close(sk->s_fd); - if (!(handle->h_flags & NL_OWN_PORT)) - release_local_port(handle->h_local.nl_pid); + if (!(sk->s_flags & NL_OWN_PORT)) + release_local_port(sk->s_local.nl_pid); - nl_cb_put(handle->h_cb); - free(handle); + nl_cb_put(sk->s_cb); + free(sk); } /** @} */ @@ -255,33 +176,60 @@ static int noop_seq_check(struct nl_msg *msg, void *arg) /** * Disable sequence number checking. - * @arg handle Netlink handle. + * @arg sk Netlink socket. * - * Disables checking of sequence numbers on the netlink handle. This is + * Disables checking of sequence numbers on the netlink socket This is * required to allow messages to be processed which were not requested by * a preceding request message, e.g. netlink events. * * @note This function modifies the NL_CB_SEQ_CHECK configuration in * the callback handle associated with the socket. */ -void nl_disable_sequence_check(struct nl_handle *handle) +void nl_socket_disable_seq_check(struct nl_sock *sk) { - nl_cb_set(handle->h_cb, NL_CB_SEQ_CHECK, + nl_cb_set(sk->s_cb, NL_CB_SEQ_CHECK, NL_CB_CUSTOM, noop_seq_check, NULL); } /** * Use next sequence number - * @arg handle Netlink handle + * @arg sk Netlink socket. * * Uses the next available sequence number and increases the counter * by one for subsequent calls. * * @return Unique serial sequence number */ -unsigned int nl_socket_use_seq(struct nl_handle *handle) +unsigned int nl_socket_use_seq(struct nl_sock *sk) +{ + return sk->s_seq_next++; +} + +/** + * Disable automatic request for ACK + * @arg sk Netlink socket. + * + * The default behaviour of a socket is to request an ACK for + * each message sent to allow for the caller to synchronize to + * the completion of the netlink operation. This function + * disables this behaviour and will result in requests being + * sent which will not have the NLM_F_ACK flag set automatically. + * However, it is still possible for the caller to set the + * NLM_F_ACK flag explicitely. + */ +void nl_socket_disable_auto_ack(struct nl_sock *sk) +{ + sk->s_flags |= NL_NO_AUTO_ACK; +} + +/** + * Enable automatic request for ACK (default) + * @arg sk Netlink socket. + * @see nl_socket_disable_auto_ack + */ +void nl_socket_enable_auto_ack(struct nl_sock *sk) { - return handle->h_seq_next++; + sk->s_flags &= ~NL_NO_AUTO_ACK; } /** @} */ @@ -291,31 +239,31 @@ unsigned int nl_socket_use_seq(struct nl_handle *handle) * @{ */ -uint32_t nl_socket_get_local_port(struct nl_handle *handle) +uint32_t nl_socket_get_local_port(struct nl_sock *sk) { - return handle->h_local.nl_pid; + return sk->s_local.nl_pid; } /** * Set local port of socket - * @arg handle Netlink handle + * @arg sk Netlink socket. * @arg port Local port identifier * * Assigns a local port identifier to the socket. If port is 0 * a unique port identifier will be generated automatically. */ -void nl_socket_set_local_port(struct nl_handle *handle, uint32_t port) +void nl_socket_set_local_port(struct nl_sock *sk, uint32_t port) { if (port == 0) { port = generate_local_port(); - handle->h_flags &= ~NL_OWN_PORT; + sk->s_flags &= ~NL_OWN_PORT; } else { - if (!(handle->h_flags & NL_OWN_PORT)) - release_local_port(handle->h_local.nl_pid); - handle->h_flags |= NL_OWN_PORT; + if (!(sk->s_flags & NL_OWN_PORT)) + release_local_port(sk->s_local.nl_pid); + sk->s_flags |= NL_OWN_PORT; } - handle->h_local.nl_pid = port; + sk->s_local.nl_pid = port; } /** @} */ @@ -326,13 +274,14 @@ void nl_socket_set_local_port(struct nl_handle *handle, uint32_t port) */ /** - * Join a group - * @arg handle Netlink handle + * Join groups + * @arg sk Netlink socket * @arg group Group identifier * - * Joins the specified group using the modern socket option which + * Joins the specified groups using the modern socket option which * is available since kernel version 2.6.14. It allows joining an - * almost arbitary number of groups without limitation. + * almost arbitary number of groups without limitation. The list + * of groups has to be terminated by 0 (%NFNLGRP_NONE). * * Make sure to use the correct group definitions as the older * bitmask definitions for nl_join_groups() are likely to still @@ -340,61 +289,95 @@ void nl_socket_set_local_port(struct nl_handle *handle, uint32_t port) * * @return 0 on sucess or a negative error code. */ -int nl_socket_add_membership(struct nl_handle *handle, int group) +int nl_socket_add_memberships(struct nl_sock *sk, int group, ...) { int err; + va_list ap; - if (handle->h_fd == -1) - return nl_error(EBADFD, "Socket not connected"); + if (sk->s_fd == -1) + return -NLE_BAD_SOCK; - err = setsockopt(handle->h_fd, SOL_NETLINK, NETLINK_ADD_MEMBERSHIP, - &group, sizeof(group)); - if (err < 0) - return nl_error(errno, "setsockopt(NETLINK_ADD_MEMBERSHIP) " - "failed"); + va_start(ap, group); + + while (group != 0) { + if (group < 0) + return -NLE_INVAL; + + err = setsockopt(sk->s_fd, SOL_NETLINK, NETLINK_ADD_MEMBERSHIP, + &group, sizeof(group)); + if (err < 0) + return -nl_syserr2nlerr(errno); + + group = va_arg(ap, int); + } + + va_end(ap); return 0; } +int nl_socket_add_membership(struct nl_sock *sk, int group) +{ + return nl_socket_add_memberships(sk, group, 0); +} + /** - * Leave a group - * @arg handle Netlink handle + * Leave groups + * @arg sk Netlink socket * @arg group Group identifier * - * Leaves the specified group using the modern socket option - * which is available since kernel version 2.6.14. + * Leaves the specified groups using the modern socket option + * which is available since kernel version 2.6.14. The list of groups + * has to terminated by 0 (%NFNLGRP_NONE). * * @see nl_socket_add_membership * @return 0 on success or a negative error code. */ -int nl_socket_drop_membership(struct nl_handle *handle, int group) +int nl_socket_drop_memberships(struct nl_sock *sk, int group, ...) { int err; + va_list ap; - if (handle->h_fd == -1) - return nl_error(EBADFD, "Socket not connected"); + if (sk->s_fd == -1) + return -NLE_BAD_SOCK; - err = setsockopt(handle->h_fd, SOL_NETLINK, NETLINK_DROP_MEMBERSHIP, - &group, sizeof(group)); - if (err < 0) - return nl_error(errno, "setsockopt(NETLINK_DROP_MEMBERSHIP) " - "failed"); + va_start(ap, group); + + while (group != 0) { + if (group < 0) + return -NLE_INVAL; + + err = setsockopt(sk->s_fd, SOL_NETLINK, NETLINK_DROP_MEMBERSHIP, + &group, sizeof(group)); + if (err < 0) + return -nl_syserr2nlerr(errno); + + group = va_arg(ap, int); + } + + va_end(ap); return 0; } +int nl_socket_drop_membership(struct nl_sock *sk, int group) +{ + return nl_socket_drop_memberships(sk, group, 0); +} + + /** * Join multicast groups (deprecated) - * @arg handle Netlink handle. + * @arg sk Netlink socket. * @arg groups Bitmask of groups to join. * * This function defines the old way of joining multicast group which * has to be done prior to calling nl_connect(). It works on any kernel * version but is very limited as only 32 groups can be joined. */ -void nl_join_groups(struct nl_handle *handle, int groups) +void nl_join_groups(struct nl_sock *sk, int groups) { - handle->h_local.nl_groups |= groups; + sk->s_local.nl_groups |= groups; } @@ -405,14 +388,14 @@ void nl_join_groups(struct nl_handle *handle, int groups) * @{ */ -uint32_t nl_socket_get_peer_port(struct nl_handle *handle) +uint32_t nl_socket_get_peer_port(struct nl_sock *sk) { - return handle->h_peer.nl_pid; + return sk->s_peer.nl_pid; } -void nl_socket_set_peer_port(struct nl_handle *handle, uint32_t port) +void nl_socket_set_peer_port(struct nl_sock *sk, uint32_t port) { - handle->h_peer.nl_pid = port; + sk->s_peer.nl_pid = port; } /** @} */ @@ -422,44 +405,44 @@ void nl_socket_set_peer_port(struct nl_handle *handle, uint32_t port) * @{ */ -int nl_socket_get_fd(struct nl_handle *handle) +int nl_socket_get_fd(struct nl_sock *sk) { - return handle->h_fd; + return sk->s_fd; } /** - * Set file descriptor of socket handle to non-blocking state - * @arg handle Netlink socket + * Set file descriptor of socket to non-blocking state + * @arg sk Netlink socket. * * @return 0 on success or a negative error code. */ -int nl_socket_set_nonblocking(struct nl_handle *handle) +int nl_socket_set_nonblocking(struct nl_sock *sk) { - if (handle->h_fd == -1) - return nl_error(EBADFD, "Socket not connected"); + if (sk->s_fd == -1) + return -NLE_BAD_SOCK; - if (fcntl(handle->h_fd, F_SETFL, O_NONBLOCK) < 0) - return nl_error(errno, "fcntl(F_SETFL, O_NONBLOCK) failed"); + if (fcntl(sk->s_fd, F_SETFL, O_NONBLOCK) < 0) + return -nl_syserr2nlerr(errno); return 0; } /** * Enable use of MSG_PEEK when reading from socket - * @arg handle Netlink socket + * @arg sk Netlink socket. */ -void nl_socket_enable_msg_peek(struct nl_handle *handle) +void nl_socket_enable_msg_peek(struct nl_sock *sk) { - handle->h_flags |= NL_MSG_PEEK; + sk->s_flags |= NL_MSG_PEEK; } /** * Disable use of MSG_PEEK when reading from socket - * @arg handle Netlink socket + * @arg sk Netlink socket. */ -void nl_socket_disable_msg_peek(struct nl_handle *handle) +void nl_socket_disable_msg_peek(struct nl_sock *sk) { - handle->h_flags &= ~NL_MSG_PEEK; + sk->s_flags &= ~NL_MSG_PEEK; } /** @} */ @@ -469,20 +452,20 @@ void nl_socket_disable_msg_peek(struct nl_handle *handle) * @{ */ -struct nl_cb *nl_socket_get_cb(struct nl_handle *handle) +struct nl_cb *nl_socket_get_cb(struct nl_sock *sk) { - return nl_cb_get(handle->h_cb); + return nl_cb_get(sk->s_cb); } -void nl_socket_set_cb(struct nl_handle *handle, struct nl_cb *cb) +void nl_socket_set_cb(struct nl_sock *sk, struct nl_cb *cb) { - nl_cb_put(handle->h_cb); - handle->h_cb = nl_cb_get(cb); + nl_cb_put(sk->s_cb); + sk->s_cb = nl_cb_get(cb); } /** * Modify the callback handler associated to the socket - * @arg handle netlink handle + * @arg sk Netlink socket. * @arg type which type callback to set * @arg kind kind of callback * @arg func callback function @@ -490,11 +473,11 @@ void nl_socket_set_cb(struct nl_handle *handle, struct nl_cb *cb) * * @see nl_cb_set */ -int nl_socket_modify_cb(struct nl_handle *handle, enum nl_cb_type type, +int nl_socket_modify_cb(struct nl_sock *sk, enum nl_cb_type type, enum nl_cb_kind kind, nl_recvmsg_msg_cb_t func, void *arg) { - return nl_cb_set(handle->h_cb, type, kind, func, arg); + return nl_cb_set(sk->s_cb, type, kind, func, arg); } /** @} */ @@ -505,19 +488,19 @@ int nl_socket_modify_cb(struct nl_handle *handle, enum nl_cb_type type, */ /** - * Set socket buffer size of netlink handle. - * @arg handle Netlink handle. + * Set socket buffer size of netlink socket. + * @arg sk Netlink socket. * @arg rxbuf New receive socket buffer size in bytes. * @arg txbuf New transmit socket buffer size in bytes. * - * Sets the socket buffer size of a netlink handle to the specified + * Sets the socket buffer size of a netlink socket to the specified * values \c rxbuf and \c txbuf. Providing a value of \c 0 assumes a * good default value. * * @note It is not required to call this function prior to nl_connect(). * @return 0 on sucess or a negative error code. */ -int nl_set_buffer_size(struct nl_handle *handle, int rxbuf, int txbuf) +int nl_socket_set_buffer_size(struct nl_sock *sk, int rxbuf, int txbuf) { int err; @@ -527,69 +510,69 @@ int nl_set_buffer_size(struct nl_handle *handle, int rxbuf, int txbuf) if (txbuf <= 0) txbuf = 32768; - if (handle->h_fd == -1) - return nl_error(EBADFD, "Socket not connected"); + if (sk->s_fd == -1) + return -NLE_BAD_SOCK; - err = setsockopt(handle->h_fd, SOL_SOCKET, SO_SNDBUF, + err = setsockopt(sk->s_fd, SOL_SOCKET, SO_SNDBUF, &txbuf, sizeof(txbuf)); if (err < 0) - return nl_error(errno, "setsockopt(SO_SNDBUF) failed"); + return -nl_syserr2nlerr(errno); - err = setsockopt(handle->h_fd, SOL_SOCKET, SO_RCVBUF, + err = setsockopt(sk->s_fd, SOL_SOCKET, SO_RCVBUF, &rxbuf, sizeof(rxbuf)); if (err < 0) - return nl_error(errno, "setsockopt(SO_RCVBUF) failed"); + return -nl_syserr2nlerr(errno); - handle->h_flags |= NL_SOCK_BUFSIZE_SET; + sk->s_flags |= NL_SOCK_BUFSIZE_SET; return 0; } /** - * Enable/disable credential passing on netlink handle. - * @arg handle Netlink handle + * Enable/disable credential passing on netlink socket. + * @arg sk Netlink socket. * @arg state New state (0 - disabled, 1 - enabled) * * @return 0 on success or a negative error code */ -int nl_set_passcred(struct nl_handle *handle, int state) +int nl_socket_set_passcred(struct nl_sock *sk, int state) { int err; - if (handle->h_fd == -1) - return nl_error(EBADFD, "Socket not connected"); + if (sk->s_fd == -1) + return -NLE_BAD_SOCK; - err = setsockopt(handle->h_fd, SOL_SOCKET, SO_PASSCRED, + err = setsockopt(sk->s_fd, SOL_SOCKET, SO_PASSCRED, &state, sizeof(state)); if (err < 0) - return nl_error(errno, "setsockopt(SO_PASSCRED) failed"); + return -nl_syserr2nlerr(errno); if (state) - handle->h_flags |= NL_SOCK_PASSCRED; + sk->s_flags |= NL_SOCK_PASSCRED; else - handle->h_flags &= ~NL_SOCK_PASSCRED; + sk->s_flags &= ~NL_SOCK_PASSCRED; return 0; } /** * Enable/disable receival of additional packet information - * @arg handle Netlink handle + * @arg sk Netlink socket. * @arg state New state (0 - disabled, 1 - enabled) * * @return 0 on success or a negative error code */ -int nl_socket_recv_pktinfo(struct nl_handle *handle, int state) +int nl_socket_recv_pktinfo(struct nl_sock *sk, int state) { int err; - if (handle->h_fd == -1) - return nl_error(EBADFD, "Socket not connected"); + if (sk->s_fd == -1) + return -NLE_BAD_SOCK; - err = setsockopt(handle->h_fd, SOL_NETLINK, NETLINK_PKTINFO, + err = setsockopt(sk->s_fd, SOL_NETLINK, NETLINK_PKTINFO, &state, sizeof(state)); if (err < 0) - return nl_error(errno, "setsockopt(NETLINK_PKTINFO) failed"); + return -nl_syserr2nlerr(errno); return 0; } |