summaryrefslogtreecommitdiff
path: root/nuttx
diff options
context:
space:
mode:
authorGregory Nutt <gnutt@nuttx.org>2015-01-25 12:37:40 -0600
committerGregory Nutt <gnutt@nuttx.org>2015-01-25 12:37:40 -0600
commitb715c9328faf3f7c51fd1a9f0bc4cb49dcb76b7d (patch)
tree8d570a0349ab3130b00a1446785c44641624c8f7 /nuttx
parent80eba4947496faf4eeb6194e98f7cf89180f14c0 (diff)
downloadpx4-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.h77
-rw-r--r--nuttx/net/local/local_conn.c347
-rw-r--r--nuttx/net/socket/connect.c57
-rw-r--r--nuttx/net/socket/net_close.c2
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
{