diff options
author | Gregory Nutt <gnutt@nuttx.org> | 2015-01-25 12:37:40 -0600 |
---|---|---|
committer | Gregory Nutt <gnutt@nuttx.org> | 2015-01-25 12:37:40 -0600 |
commit | b715c9328faf3f7c51fd1a9f0bc4cb49dcb76b7d (patch) | |
tree | 8d570a0349ab3130b00a1446785c44641624c8f7 /nuttx | |
parent | 80eba4947496faf4eeb6194e98f7cf89180f14c0 (diff) | |
download | px4-nuttx-b715c9328faf3f7c51fd1a9f0bc4cb49dcb76b7d.tar.gz px4-nuttx-b715c9328faf3f7c51fd1a9f0bc4cb49dcb76b7d.tar.bz2 px4-nuttx-b715c9328faf3f7c51fd1a9f0bc4cb49dcb76b7d.zip |
Networking: Add local Unix domain socket connection logic
Diffstat (limited to 'nuttx')
-rw-r--r-- | nuttx/net/local/local.h | 77 | ||||
-rw-r--r-- | nuttx/net/local/local_conn.c | 347 | ||||
-rw-r--r-- | nuttx/net/socket/connect.c | 57 | ||||
-rw-r--r-- | nuttx/net/socket/net_close.c | 2 |
4 files changed, 462 insertions, 21 deletions
diff --git a/nuttx/net/local/local.h b/nuttx/net/local/local.h index 864f09a90..fa2936b41 100644 --- a/nuttx/net/local/local.h +++ b/nuttx/net/local/local.h @@ -60,19 +60,70 @@ enum local_type_e { - LOCAL_TYPE_UNNAMED = 0, /* A Unix socket that is not bound to any name */ + LOCAL_TYPE_UNTYPED = 0, /* Type is not determined until the socket is bound */ + LOCAL_TYPE_UNNAMED, /* A Unix socket that is not bound to any name */ LOCAL_TYPE_PATHNAME, /* lc_path holds a null terminated string */ LOCAL_TYPE_ABSTRACT /* lc_path is length zero */ }; +/* The state of a Unix socket */ + +enum local_state_s +{ + /* Common states */ + + LOCAL_STATE_UNBOUND = 0, /* Created by socket, but not bound */ + LOCAL_STATE_BOUND, /* Bound to an pipe */ + + /* SOCK_STREAM only */ + + LOCAL_STATE_LISTENING, /* Server listening for connections */ + LOCAL_STATE_CLOSED, /* Server closed, no longer connected */ + LOCAL_STATE_ACCEPT, /* Client waiting for a connection */ + LOCAL_STATE_CONNECTED, /* Client connected */ + LOCAL_STATE_DISCONNECTED /* Client disconnected */ +}; + /* Representation of a local connection */ struct local_conn_s { + /* Fields common to SOCK_STREAM and SOCK_DGRAM */ + + dq_entry_t lc_node; /* Supports a doubly linked list */ uint8_t lc_crefs; /* Reference counts on this instance */ + uint8_t lc_family; /* SOCK_STREAM or SOCK_DGRAM */ uint8_t lc_type; /* See enum local_type_e */ + uint8_t lc_state; /* See enum local_state_e */ int16_t lc_fd; /* File descriptor of underlying pipe */ char lc_path[UNIX_PATH_MAX]; /* Path assigned by bind() */ + + /* SOCK_STREAM fields common to both client and server */ + + sem_t lc_waitsem; /* Use to wait for a connection to be accepted */ + + /* Union of fields unique to SOCK_STREAM client and servers */ + + union + { + /* Fields unique to the SOCK_STREAM server side */ + + struct + { + uint8_t lc_pending; /* Number of pending connections */ + uint8_t lc_backlog; /* Maximum number of pending connections */ + dq_queue_t lc_waiters; /* List of connections waiting to be accepted */ + dq_queue_t lc_conns; /* List of connections */ + } server; + + /* Fields unique to the client side */ + + struct + { + FAR struct local_conn_s *lc_server; /* Server connection */ + volatile int lc_result; /* Result of the connection operation */ + } client; + } u; }; /**************************************************************************** @@ -103,8 +154,7 @@ struct socket; /* Forward reference */ * ****************************************************************************/ -/* void local_initialize(void) */ -#define local_initialize() +void local_initialize(void); /**************************************************************************** * Name: local_alloc @@ -154,18 +204,29 @@ int local_bind(FAR struct local_conn_s *conn, * and recvfrom. * * Input Parameters: - * conn - A reference to local connection structure + * client - A reference to the client-side local connection structure * addr - The address of the remote host. * - * Assumptions: - * This function is called user code. Interrupts may be enabled. - * ****************************************************************************/ -int local_connect(FAR struct local_conn_s *conn, +int local_connect(FAR struct local_conn_s *client, FAR const struct sockaddr *addr); /**************************************************************************** + * Name: local_release + * + * Description: + * If the local, Unix domain socket is in the connected state, then + * disconnect it. Release the local connection structure in any event + * + * Input Parameters: + * conn - A reference to local connection structure + * + ****************************************************************************/ + +int local_release(FAR struct local_conn_s *conn); + +/**************************************************************************** * Name: psock_local_send * * Description: diff --git a/nuttx/net/local/local_conn.c b/nuttx/net/local/local_conn.c index 74fc61afe..159cac99c 100644 --- a/nuttx/net/local/local_conn.c +++ b/nuttx/net/local/local_conn.c @@ -40,8 +40,11 @@ #include <nuttx/config.h> #if defined(CONFIG_NET) && defined(CONFIG_NET_LOCAL) +#include <semaphore.h> #include <string.h> #include <assert.h> +#include <errno.h> +#include <queue.h> #include <debug.h> #include <nuttx/kmalloc.h> @@ -52,11 +55,101 @@ * Private Data ****************************************************************************/ +/* A list of all allocated packet socket connections */ + +static dq_queue_t g_local_listeners; + /**************************************************************************** * Private Functions ****************************************************************************/ /**************************************************************************** + * Name: _local_semtake() and _local_semgive() + * + * Description: + * Take/give semaphore + * + ****************************************************************************/ + +static inline void _local_semtake(sem_t *sem) +{ + /* Take the semaphore (perhaps waiting) */ + + while (net_lockedwait(sem) != 0) + { + /* The only case that an error should occur here is if + * the wait was awakened by a signal. + */ + + ASSERT(*get_errno_ptr() == EINTR); + } +} + +#define _local_semgive(sem) sem_post(sem) + +/**************************************************************************** + * Name: local_stream_connect + * + * Description: + * Find a local connection structure that is the appropriate "server" + * connection to be used with the provided "client" connection. + * + * Returned Values: + * Zero (OK) returned on success; A negated errno value is returned on a + * failure. Possible failures include: + * + * Assumptions: + * The network is locked on entry, unlocked on return. This logic is + * an integral part of the lock_connect() implementation and was + * separated out only to improve readability. + * + ****************************************************************************/ + +int inline local_stream_connect(FAR struct local_conn_s *client, + FAR struct local_conn_s *server, + net_lock_t state) +{ + int ret; + + /* Has server backlog been reached? + * NOTE: The backlog will be zero if listen() has never been called by the + * server. + */ + + if (server->lc_state != LOCAL_STATE_LISTENING || + server->u.server.lc_pending >= server->u.server.lc_backlog) + { + net_unlock(state); + return -ECONNREFUSED; + } + + server->u.server.lc_pending++; + DEBUGASSERT(server->u.server.lc_pending != 0); + + /* Add ourself to the list of waiting connections and notify the server. */ + + dq_addlast(&client->lc_node, &server->u.server.lc_waiters); + client->lc_state = LOCAL_STATE_ACCEPT; + _local_semgive(&server->lc_waitsem); + net_unlock(state); + + /* Wait for the server to accept the connections */ + + client->u.client.lc_result = -EBUSY; + do + { + _local_semtake(&client->lc_waitsem); + ret = client->u.client.lc_result; + } + while (ret == -EBUSY); + + /* Was the connection successful? */ + + client->lc_state = (ret < 0 ? LOCAL_STATE_BOUND : LOCAL_STATE_CONNECTED); + return ret; +} + +/**************************************************************************** * Public Functions ****************************************************************************/ @@ -69,7 +162,10 @@ * ****************************************************************************/ -/* void local_initialize(void) */ + void local_initialize(void) +{ + dq_init(&g_local_listeners); +} /**************************************************************************** * Name: local_alloc() @@ -88,9 +184,10 @@ FAR struct local_conn_s *local_alloc(void) if (conn) { - /* Make sure that the pipe is marked closed */ + /* Initialize non-zero elements the new connection structure */ conn->lc_fd = -1; + sem_init(&conn->lc_waitsem, 0, 0); } return conn; @@ -108,7 +205,19 @@ FAR struct local_conn_s *local_alloc(void) void local_free(FAR struct local_conn_s *conn) { DEBUGASSERT(conn != NULL); - free(conn); + + /* Make sure that the pipe is closed */ + + if (conn->lc_fd >= 0) + { + close(conn->lc_fd); + } + + sem_destroy(&conn->lc_waitsem); + + /* And free the connection structure */ + + kmm_free(conn); } /**************************************************************************** @@ -130,6 +239,14 @@ int local_bind(FAR struct local_conn_s *conn, DEBUGASSERT(conn && unaddr && unaddr->sun_family == AF_LOCAL && addrlen >= sizeof(sa_family_t)); + /* Save the address family */ + + conn->lc_family = unaddr->sun_family; + + /* No determine the type of the Unix domain socket by comparing the size + * of the address description. + */ + if (addrlen == sizeof(sa_family_t)) { /* No sun_path... This is an un-named Unix domain socket */ @@ -150,7 +267,7 @@ int local_bind(FAR struct local_conn_s *conn, { /* This is an normal, pathname Unix domain socket */ - conn->lc_type = LOCAL_TYPE_PATHNAME; + conn->lc_type = LOCAL_TYPE_PATHNAME; /* Copy the path into the connection structure */ @@ -159,8 +276,230 @@ int local_bind(FAR struct local_conn_s *conn, } } + conn->lc_state = LOCAL_STATE_BOUND; return OK; } +/**************************************************************************** + * Name: local_connect + * + * Description: + * Find a local connection structure that is the appropriate "server" + * connection to be used with the provided "client" connection. + * + * Returned Values: + * Zero (OK) returned on success; A negated errno value is returned on a + * failure. Possible failures include: + * + * EISCONN - The specified socket is connection-mode and is already + * connected. + * EADDRNOTAVAIL - The specified address is not available from the + * local machine. + * ECONNREFUSED - The target address was not listening for connections or + * refused the connection request because the connection backlog has + * been exceeded. + * + ****************************************************************************/ + +int local_connect(FAR struct local_conn_s *client, + FAR const struct sockaddr *addr) +{ + FAR struct local_conn_s *conn; + net_lock_t state; + + DEBUGASSERT(client); + + if (client->lc_state == LOCAL_STATE_ACCEPT || + client->lc_state == LOCAL_STATE_CONNECTED) + { + return -EISCONN; + } + + /* Find the matching server connection */ + + state = net_lock(); + for(conn = (FAR struct local_conn_s *)g_local_listeners.head; + conn; + conn = (FAR struct local_conn_s *)dq_next(&conn->lc_node)) + { + /* Skip over connections that that have not yet been bound, + * are or a different address family, or are of a different type. + */ + + if (conn->lc_state == LOCAL_STATE_UNBOUND || + conn->lc_state == LOCAL_STATE_CLOSED || + conn->lc_family != client->lc_family || + conn->lc_type != client->lc_type) + { + continue; + } + + /* Handle according to the connection type */ + + switch (client->lc_type) + { + case LOCAL_TYPE_UNNAMED: /* A Unix socket that is not bound to any name */ + case LOCAL_TYPE_ABSTRACT: /* lc_path is length zero */ + { +#warning Missing logic + net_unlock(state); + return OK; + } + break; + + case LOCAL_TYPE_PATHNAME: /* lc_path holds a null terminated string */ + { + if (strncmp(client->lc_path, conn->lc_path, UNIX_PATH_MAX-1) == 0) + { + /* We have to do more for the SOCK_STREAM family */ + + if (conn->lc_family == SOCK_STREAM) + { + return local_stream_connect(client, conn, state); + } + + net_unlock(state); + return OK; + } + } + break; + + default: /* Bad, memory must be corrupted */ + DEBUGPANIC(); /* PANIC if debug on, else fall through */ + + case LOCAL_TYPE_UNTYPED: /* Type is not determined until the socket is bound */ + { + net_unlock(state); + return -EINVAL; + } + } + } + + net_unlock(state); + return -EADDRNOTAVAIL; +} + +/**************************************************************************** + * Name: local_release + * + * Description: + * If the local, Unix domain socket is in the connected state, then + * disconnect it. Release the local connection structure in any event + * + * Input Parameters: + * conn - A reference to local connection structure + * + ****************************************************************************/ + +int local_release(FAR struct local_conn_s *conn) +{ + net_lock_t state; + + /* There should be no references on this structure */ + + DEBUGASSERT(conn->lc_crefs == 0); + state = net_lock(); + + /* We should not bet here with states LOCAL_STATE_CLOSED or with + * LOCAL_STATE_ACCEPT. Those are internal states that should be atomic + * with respect to socket operations. + */ + + DEBUGASSERT(conn->lc_state != LOCAL_STATE_CLOSED && + conn->lc_state != LOCAL_STATE_ACCEPT); + + /* If the socket is connected (SOCK_STREAM client), then disconnect it */ + + if (conn->lc_state == LOCAL_STATE_CONNECTED || + conn->lc_state == LOCAL_STATE_DISCONNECTED) + { + FAR struct local_conn_s *server; + + DEBUGASSERT(conn->lc_family == SOCK_STREAM); + + server = conn->u.client.lc_server; + DEBUGASSERT(server && + (server->lc_state == LOCAL_STATE_LISTENING || + server->lc_state == LOCAL_STATE_CLOSED) && + !dq_empty(&server->u.server.lc_conns)); + + /* Remove ourself from the list of connections */ + + dq_rem(&conn->lc_node, &server->u.server.lc_conns); + + /* Is the list of pending connections now empty? Was the connection + * already closed? + */ + + if (dq_empty(&server->u.server.lc_waiters) && + server->lc_state == LOCAL_STATE_CLOSED) + { + /* Yes, free the server connection as well */ + + local_free(server); + } + + /* Now we can free this connection structure */ + + local_free(conn); + } + + /* Is the socket is listening socket (SOCK_STREAM server) */ + + else if (conn->lc_state == LOCAL_STATE_LISTENING) + { + FAR struct local_conn_s *client; + FAR struct local_conn_s *next; + + DEBUGASSERT(conn->lc_family == SOCK_STREAM); + + /* Are there still clients waiting for a connection to the server? */ + + for (client = (FAR struct local_conn_s *)conn->u.server.lc_waiters.head; + client; + client = (FAR struct local_conn_s *)dq_next(&client->lc_node)) + { + client->u.client.lc_result = -ENETUNREACH; + _local_semgive(&client->lc_waitsem); + conn->lc_state = LOCAL_STATE_CLOSED; + } + + conn->u.server.lc_pending = 0; + + /* Disconnect any previous client connections */ + + for (client = (FAR struct local_conn_s *)conn->u.server.lc_conns.head; + client; + client = next) + { + next = (FAR struct local_conn_s *)dq_next(&client->lc_node); + dq_rem(&client->lc_node, &conn->u.server.lc_conns); + client->lc_state = LOCAL_STATE_DISCONNECTED; + } + + /* Can we free the connection structure now? We cannot + * if there are still pending connection requested to + * be resolved. + */ + + conn->u.server.lc_backlog = 0; + if (conn->lc_state == LOCAL_STATE_CLOSED) + { + local_free(conn); + } + } + + /* For the remaining states (LOCAL_STATE_UNBOUND and LOCAL_STATE_UNBOUND), + * we simply free the connection structure. + */ + + else + { + local_free(conn); + } + + net_unlock(state); + return OK; +} #endif /* CONFIG_NET && CONFIG_NET_LOCAL */ diff --git a/nuttx/net/socket/connect.c b/nuttx/net/socket/connect.c index e152df888..88535210a 100644 --- a/nuttx/net/socket/connect.c +++ b/nuttx/net/socket/connect.c @@ -1,7 +1,7 @@ /**************************************************************************** * net/socket/connect.c * - * Copyright (C) 2007-2012 Gregory Nutt. All rights reserved. + * Copyright (C) 2007-2012, 2015 Gregory Nutt. All rights reserved. * Author: Gregory Nutt <gnutt@nuttx.org> * * Redistribution and use in source and binary forms, with or without @@ -56,6 +56,7 @@ #include "devif/devif.h" #include "tcp/tcp.h" #include "udp/udp.h" +#include "local/local.h" #include "socket/socket.h" /**************************************************************************** @@ -511,7 +512,7 @@ int psock_connect(FAR struct socket *psock, FAR const struct sockaddr *addr, switch (psock->s_type) { -#ifdef CONFIG_NET_TCP +#if defined(CONFIG_NET_TCP) || defined(CONFIG_NET_LOCAL) case SOCK_STREAM: { /* Verify that the socket is not already connected */ @@ -522,9 +523,30 @@ int psock_connect(FAR struct socket *psock, FAR const struct sockaddr *addr, goto errout; } - /* Its not ... connect it */ + /* It's not ... connect it */ + +#ifdef CONFIG_NET_LOCAL +#ifdef CONFIG_NET_TCP + if (psock->s_domain == PF_LOCAL) +#endif + { + /* Connect to the local Unix domain server */ + + ret = local_connect(psock->s_conn, addr); + } +#endif /* CONFIG_NET_LOCAL */ + +#ifdef CONFIG_NET_TCP +#ifdef CONFIG_NET_LOCAL + else +#endif + { + /* Connect the TCP/IP socket */ + + ret = psock_tcp_connect(psock, addr); + } +#endif /* CONFIG_NET_TCP */ - ret = psock_tcp_connect(psock, addr); if (ret < 0) { err = -ret; @@ -532,12 +554,31 @@ int psock_connect(FAR struct socket *psock, FAR const struct sockaddr *addr, } } break; -#endif +#endif /* CONFIG_NET_TCP || CONFIG_NET_LOCAL */ -#ifdef CONFIG_NET_UDP +#if defined(CONFIG_NET_UDP) || defined(CONFIG_NET_LOCAL) case SOCK_DGRAM: { - ret = udp_connect(psock->s_conn, addr); +#ifdef CONFIG_NET_LOCAL +#ifdef CONFIG_NET_UDP + if (psock->s_domain == PF_LOCAL) +#endif + { + /* Perform the datagram connection logic */ + + ret = local_connect(psock->s_conn, addr); + } +#endif /* CONFIG_NET_LOCAL */ + +#ifdef CONFIG_NET_UDP +#ifdef CONFIG_NET_LOCAL + else +#endif + { + ret = udp_connect(psock->s_conn, addr); + } +#endif /* CONFIG_NET_UDP */ + if (ret < 0) { err = -ret; @@ -545,7 +586,7 @@ int psock_connect(FAR struct socket *psock, FAR const struct sockaddr *addr, } } break; -#endif +#endif /* CONFIG_NET_UDP || CONFIG_NET_LOCAL */ default: err = EBADF; diff --git a/nuttx/net/socket/net_close.c b/nuttx/net/socket/net_close.c index bab3958e3..e33853fa7 100644 --- a/nuttx/net/socket/net_close.c +++ b/nuttx/net/socket/net_close.c @@ -480,7 +480,7 @@ static void local_close(FAR struct socket *psock) if (conn->lc_crefs <= 1) { conn->lc_crefs = 0; - local_free(conn); + local_release(conn); } else { |