add socket nl_connect_fd() & nl_create_fd()

-    Added option to create socket (fd) without bind.
     It is now possible to forward the socket fd to another child process...
     ...later use nl_connect_fd() to connect to socket from the child process.

-    Added option to disable CLOEXEC even if defined (in socket.h)
     'nl_socket_enable_cloexec' & 'nl_socket_disable_cloexec'
     No change to current default behavior.

 Signed-off-by: Sagi Lowenhardt <sagil@infinidat.com>
This commit is contained in:
Sagi Lowenhardt 2015-02-17 15:22:27 +02:00 committed by Thomas Haller
parent 9614acf4c4
commit f91e6959ea
5 changed files with 90 additions and 21 deletions

View file

@ -77,6 +77,7 @@ struct nl_sock
int s_flags;
struct nl_cb * s_cb;
size_t s_bufsize;
int s_cloexec;
};
struct nl_cache

View file

@ -49,6 +49,8 @@ extern struct nl_dump_params nl_debug_dp;
/* Connection Management */
extern int nl_connect(struct nl_sock *, int);
extern int nl_create_fd(struct nl_sock *, int);
extern int nl_connect_fd(struct nl_sock *, int, int);
extern void nl_close(struct nl_sock *);
/* Send */

View file

@ -63,6 +63,8 @@ extern int nl_socket_get_fd(const struct nl_sock *);
extern int nl_socket_set_nonblocking(const struct nl_sock *);
extern void nl_socket_enable_msg_peek(struct nl_sock *);
extern void nl_socket_disable_msg_peek(struct nl_sock *);
extern int nl_socket_enable_cloexec(struct nl_sock *);
extern void nl_socket_disable_cloexec(struct nl_sock *);
#ifdef __cplusplus
}

100
lib/nl.c
View file

@ -63,19 +63,57 @@
*/
/**
* Create file descriptor and bind socket.
* Create file descriptor.
* @arg sk Netlink socket (required)
* @arg protocol Netlink protocol to use (required)
*
* Creates a new Netlink socket using `socket()` . Fails if
* the socket is already connected.
*/
int nl_create_fd(struct nl_sock *sk, int protocol)
{
int err, flags = 0;
int errsv;
char buf[64];
#ifdef SOCK_CLOEXEC
if (sk->s_cloexec == 1)
flags |= SOCK_CLOEXEC;
#endif
if (sk->s_fd != -1)
return -NLE_BAD_SOCK;
sk->s_fd = socket(AF_NETLINK, SOCK_RAW | flags, protocol);
if (sk->s_fd < 0) {
errsv = errno;
NL_DBG(4, "nl_connect(%p): socket() failed with %d (%s)\n", sk, errsv,
strerror_r(errsv, buf, sizeof(buf)));
err = -nl_syserr2nlerr(errsv);
goto errout;
}
return 0;
errout:
if (sk->s_fd != -1) {
close(sk->s_fd);
sk->s_fd = -1;
}
return err;
}
/**
* Create file descriptor and bind socket.
* @arg sk Netlink socket (required)
* @arg protocol Netlink protocol to use (required)
*
* Creates a new Netlink socket using `socket()` and binds the socket to the
* protocol and local port specified in the `sk` socket object. Fails if
* the socket is already connected.
*
* @note If available, the `close-on-exec` (`SOCK_CLOEXEC`) feature is enabled
* automatically on the new file descriptor. This causes the socket to
* be closed automatically if any of the `exec` family functions succeed.
* This is essential for multi threaded programs.
*
* @note The local port (`nl_socket_get_local_port()`) is unspecified after
* creating a new socket. It only gets determined when accessing the
* port the first time or during `nl_connect()`. When nl_connect()
@ -95,27 +133,47 @@
*/
int nl_connect(struct nl_sock *sk, int protocol)
{
int err, flags = 0;
int err = nl_create_fd(sk, protocol);
if (err != 0)
return err;
return nl_connect_fd(sk, protocol, sk->s_fd);
}
/**
* @arg sk Netlink socket (required)
* @arg protocol Netlink protocol to use (required)
* @arg fd Socket file descriptor to use (required)
*
* @note The local port (`nl_socket_get_local_port()`) is unspecified after
* creating a new socket. It only gets determined when accessing the
* port the first time or during `nl_connect_fd()`. When nl_connect_fd()
* fails during `bind()` due to `ADDRINUSE`, it will retry with
* different ports if the port is unspecified. Unless you want to enforce
* the use of a specific local port, don't access the local port (or
* reset it to `unspecified` by calling `nl_socket_set_local_port(sk, 0)`).
* This capability is indicated by
* `%NL_CAPABILITY_NL_CONNECT_RETRY_GENERATE_PORT_ON_ADDRINUSE`.
*
* @see nl_socket_alloc()
* @see nl_close()
*
* @return 0 on success or a negative error code.
*
* @retval -NLE_BAD_SOCK Socket is not connected
*/
int nl_connect_fd(struct nl_sock *sk, int protocol, int fd)
{
int err = 0;
int errsv;
socklen_t addrlen;
struct sockaddr_nl local = { 0 };
char buf[64];
#ifdef SOCK_CLOEXEC
flags |= SOCK_CLOEXEC;
#endif
if (fd < 0)
return -NLE_BAD_SOCK;
if (sk->s_fd != -1)
return -NLE_BAD_SOCK;
sk->s_fd = socket(AF_NETLINK, SOCK_RAW | flags, protocol);
if (sk->s_fd < 0) {
errsv = errno;
NL_DBG(4, "nl_connect(%p): socket() failed with %d (%s)\n", sk, errsv,
strerror_r(errsv, buf, sizeof(buf)));
err = -nl_syserr2nlerr(errsv);
goto errout;
}
sk->s_fd = fd;
err = nl_socket_set_buffer_size(sk, 0, 0);
if (err < 0)

View file

@ -192,6 +192,12 @@ static struct nl_sock *__alloc_socket(struct nl_cb *cb)
sk->s_peer.nl_family = AF_NETLINK;
sk->s_seq_expect = sk->s_seq_next = time(NULL);
#ifdef SOCK_CLOEXEC
sk->s_cloexec = 1;
#else
sk->s_cloexec = 0;
#endif
/* the port is 0 (unspecified), meaning NL_OWN_PORT */
sk->s_flags = NL_OWN_PORT;