diff options
-rw-r--r-- | include/netlink-private/types.h | 1 | ||||
-rw-r--r-- | include/netlink/netlink.h | 2 | ||||
-rw-r--r-- | include/netlink/socket.h | 2 | ||||
-rw-r--r-- | lib/nl.c | 100 | ||||
-rw-r--r-- | lib/socket.c | 6 |
5 files changed, 90 insertions, 21 deletions
diff --git a/include/netlink-private/types.h b/include/netlink-private/types.h index 54f06b5..7d044f1 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 f8f2082..7b6cb60 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 1007eba..bda0ff3 100644 --- a/include/netlink/socket.h +++ b/include/netlink/socket.h @@ -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 } @@ -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 (sk->s_fd != -1) - return -NLE_BAD_SOCK; + 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 (%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) diff --git a/lib/socket.c b/lib/socket.c index 628a96d..01cc219 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; |