From 7176446e1ae99bd4187d3c6d7088cebc107aeffd Mon Sep 17 00:00:00 2001 From: Sagi Lowenhardt Date: Sun, 15 Feb 2015 13:30:47 +0200 Subject: [PATCH] add nl_connect_fd() & nl_create_fd() support Signed-off-by: Sagi Lowenhardt --- include/netlink-private/types.h | 1 + include/netlink/netlink.h | 2 + include/netlink/socket.h | 3 + lib/nl.c | 98 ++++++++++++++++++++++++++------- lib/socket.c | 6 ++ 5 files changed, 89 insertions(+), 21 deletions(-) diff --git a/include/netlink-private/types.h b/include/netlink-private/types.h index b4dc8acb5..682c6fcf0 100644 --- a/include/netlink-private/types.h +++ b/include/netlink-private/types.h @@ -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 diff --git a/include/netlink/netlink.h b/include/netlink/netlink.h index f8f2082f2..7b6cb60cf 100644 --- a/include/netlink/netlink.h +++ b/include/netlink/netlink.h @@ -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 */ diff --git a/include/netlink/socket.h b/include/netlink/socket.h index 1007eba03..cbe4d1755 100644 --- a/include/netlink/socket.h +++ b/include/netlink/socket.h @@ -64,6 +64,9 @@ 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 } #endif diff --git a/lib/nl.c b/lib/nl.c index fade84818..713fb5e74 100644 --- a/lib/nl.c +++ b/lib/nl.c @@ -63,19 +63,54 @@ */ /** - * 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; + +#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, "create_socket(%p): socket() failed with %d\n", sk, errsv); + 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,24 +130,45 @@ */ int nl_connect(struct nl_sock *sk, int protocol) { - int err, flags = 0; - int errsv; - socklen_t addrlen; + int err = nl_create_fd(sk, protocol); + if (err != 0) + return err; -#ifdef SOCK_CLOEXEC - flags |= SOCK_CLOEXEC; -#endif + return nl_connect_fd(sk, protocol, sk->s_fd); +} - if (sk->s_fd != -1) - return -NLE_BAD_SOCK; +/** + * @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; + + if (fd < 0) + 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\n", sk, errsv); - err = -nl_syserr2nlerr(errsv); - goto errout; - } + sk->s_fd = fd; if (!(sk->s_flags & NL_SOCK_BUFSIZE_SET)) { err = nl_socket_set_buffer_size(sk, 0, 0); diff --git a/lib/socket.c b/lib/socket.c index 3a41caa6c..c20d66ea6 100644 --- a/lib/socket.c +++ b/lib/socket.c @@ -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;