summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--sys/netgraph/ng_ksocket.c415
-rw-r--r--sys/netgraph/ng_ksocket.h17
2 files changed, 383 insertions, 49 deletions
diff --git a/sys/netgraph/ng_ksocket.c b/sys/netgraph/ng_ksocket.c
index 5baefa2..c29aa7d 100644
--- a/sys/netgraph/ng_ksocket.c
+++ b/sys/netgraph/ng_ksocket.c
@@ -78,17 +78,31 @@ MALLOC_DEFINE(M_NETGRAPH_KSOCKET, "netgraph_ksock", "netgraph ksock node ");
/* Node private data */
struct ng_ksocket_private {
+ node_p node;
hook_p hook;
struct socket *so;
+ LIST_HEAD(, ng_ksocket_private) embryos;
+ LIST_ENTRY(ng_ksocket_private) siblings;
+ u_int32_t flags;
+ u_int32_t response_token;
+ ng_ID_t response_addr;
};
typedef struct ng_ksocket_private *priv_p;
+/* Flags for priv_p */
+#define KSF_CONNECTING 0x00000001 /* Waiting for connection complete */
+#define KSF_ACCEPTING 0x00000002 /* Waiting for accept complete */
+#define KSF_EOFSEEN 0x00000004 /* Have sent 0-length EOF mbuf */
+#define KSF_CLONED 0x00000008 /* Cloned from an accepting socket */
+#define KSF_EMBRYONIC 0x00000010 /* Cloned node with no hooks yet */
+
/* Netgraph node methods */
static ng_constructor_t ng_ksocket_constructor;
static ng_rcvmsg_t ng_ksocket_rcvmsg;
static ng_shutdown_t ng_ksocket_shutdown;
static ng_newhook_t ng_ksocket_newhook;
static ng_rcvdata_t ng_ksocket_rcvdata;
+static ng_connect_t ng_ksocket_connect;
static ng_disconnect_t ng_ksocket_disconnect;
/* Alias structure */
@@ -139,9 +153,13 @@ static const struct ng_ksocket_alias ng_ksocket_protos[] = {
};
/* Helper functions */
+static int ng_ksocket_check_accept(priv_p);
+static void ng_ksocket_finish_accept(priv_p);
static void ng_ksocket_incoming(struct socket *so, void *arg, int waitflag);
static int ng_ksocket_parse(const struct ng_ksocket_alias *aliases,
const char *s, int family);
+static void ng_ksocket_incoming2(node_p node, hook_p hook,
+ void *arg1, int waitflag);
/************************************************************************
STRUCT SOCKADDR PARSE TYPE
@@ -402,6 +420,14 @@ static const struct ng_parse_type ng_ksocket_sockopt_type = {
&ng_ksocket_sockopt_type_info,
};
+/* Parse type for struct ng_ksocket_accept */
+static const struct ng_parse_struct_info ng_ksocket_accept_type_info
+ = NGM_KSOCKET_ACCEPT_INFO;
+static const struct ng_parse_type ng_ksocket_accept_type = {
+ &ng_parse_struct_type,
+ &ng_ksocket_accept_type_info
+};
+
/* List of commands and how to convert arguments to/from ASCII */
static const struct ng_cmdlist ng_ksocket_cmds[] = {
{
@@ -423,14 +449,14 @@ static const struct ng_cmdlist ng_ksocket_cmds[] = {
NGM_KSOCKET_ACCEPT,
"accept",
NULL,
- &ng_ksocket_sockaddr_type
+ &ng_ksocket_accept_type
},
{
NGM_KSOCKET_COOKIE,
NGM_KSOCKET_CONNECT,
"connect",
&ng_ksocket_sockaddr_type,
- NULL
+ &ng_parse_int32_type
},
{
NGM_KSOCKET_COOKIE,
@@ -473,7 +499,7 @@ static struct ng_type ng_ksocket_typestruct = {
ng_ksocket_shutdown,
ng_ksocket_newhook,
NULL,
- NULL,
+ ng_ksocket_connect,
ng_ksocket_rcvdata,
ng_ksocket_disconnect,
ng_ksocket_cmds
@@ -488,6 +514,8 @@ NETGRAPH_INIT(ksocket, &ng_ksocket_typestruct);
/*
* Node type constructor
+ * The NODE part is assumed to be all set up.
+ * There is already a reference to the node for us.
*/
static int
ng_ksocket_constructor(node_p node)
@@ -500,6 +528,9 @@ ng_ksocket_constructor(node_p node)
if (priv == NULL)
return (ENOMEM);
+ LIST_INIT(&priv->embryos);
+ /* cross link them */
+ priv->node = node;
NG_NODE_SET_PRIVATE(node, priv);
/* Done */
@@ -526,37 +557,96 @@ ng_ksocket_newhook(node_p node, hook_p hook, const char *name0)
if (priv->hook != NULL)
return (EISCONN);
- /* Extract family, type, and protocol from hook name */
- snprintf(name, sizeof(name), "%s", name0);
- s1 = name;
- if ((s2 = index(s1, '/')) == NULL)
- return (EINVAL);
- *s2++ = '\0';
- if ((family = ng_ksocket_parse(ng_ksocket_families, s1, 0)) == -1)
- return (EINVAL);
- s1 = s2;
- if ((s2 = index(s1, '/')) == NULL)
- return (EINVAL);
- *s2++ = '\0';
- if ((type = ng_ksocket_parse(ng_ksocket_types, s1, 0)) == -1)
- return (EINVAL);
- s1 = s2;
- if ((protocol = ng_ksocket_parse(ng_ksocket_protos, s1, family)) == -1)
- return (EINVAL);
+ if (priv->flags & KSF_CLONED) {
+ if (priv->flags & KSF_EMBRYONIC) {
+ /* Remove ourselves from our parent's embryo list */
+ LIST_REMOVE(priv, siblings);
+ priv->flags &= ~KSF_EMBRYONIC;
+ }
+ } else {
+ /* Extract family, type, and protocol from hook name */
+ snprintf(name, sizeof(name), "%s", name0);
+ s1 = name;
+ if ((s2 = index(s1, '/')) == NULL)
+ return (EINVAL);
+ *s2++ = '\0';
+ family = ng_ksocket_parse(ng_ksocket_families, s1, 0);
+ if (family == -1)
+ return (EINVAL);
+ s1 = s2;
+ if ((s2 = index(s1, '/')) == NULL)
+ return (EINVAL);
+ *s2++ = '\0';
+ type = ng_ksocket_parse(ng_ksocket_types, s1, 0);
+ if (type == -1)
+ return (EINVAL);
+ s1 = s2;
+ protocol = ng_ksocket_parse(ng_ksocket_protos, s1, family);
+ if (protocol == -1)
+ return (EINVAL);
+
+ /* Create the socket */
+ error = socreate(family, &priv->so, type, protocol, p);
+ if (error != 0)
+ return (error);
- /* Create the socket */
- if ((error = socreate(family, &priv->so, type, protocol, p)) != 0)
- return (error);
+ /* XXX call soreserve() ? */
+
+ }
+
+ /* OK */
+ priv->hook = hook;
+ return(0);
+}
- /* XXX call soreserve() ? */
+static int
+ng_ksocket_connect(hook_p hook)
+{
+ node_p node = NG_HOOK_NODE(hook);
+ const priv_p priv = NG_NODE_PRIVATE(node);
+ struct socket *const so = priv->so;
- /* Add our hook for incoming data */
+ /* Add our hook for incoming data and other events */
priv->so->so_upcallarg = (caddr_t)node;
priv->so->so_upcall = ng_ksocket_incoming;
priv->so->so_rcv.sb_flags |= SB_UPCALL;
+ priv->so->so_snd.sb_flags |= SB_UPCALL;
+ priv->so->so_state |= SS_NBIO;
+ /*
+ * --Original comment--
+ * On a cloned socket we may have already received one or more
+ * upcalls which we couldn't handle without a hook. Handle
+ * those now.
+ * We cannot call the upcall function directly
+ * from here, because until this function has returned our
+ * hook isn't connected.
+ *
+ * ---meta comment for -current ---
+ * XXX This is dubius.
+ * Upcalls between the time that the hook was
+ * first created and now (on another processesor) will
+ * be earlier on the queue than the request to finalise the hook.
+ * By the time the hook is finalised,
+ * The queued upcalls will have happenned and the code
+ * will have discarded them because of a lack of a hook.
+ * (socket not open).
+ *
+ * This is a bad byproduct of the complicated way in which hooks
+ * are now created (3 daisy chained async events).
+ *
+ * Since we are a netgraph operation
+ * We know that we hold a lock on this node. This forces the
+ * request we make below to be queued rather than implemented
+ * immediatly which will cause the upcall function to be called a bit
+ * later.
+ * However, as we will run any waiting queued operations immediatly
+ * after doing this one, if we have not finalised the other end
+ * of the hook, those queued operations will fail.
+ */
+ if (priv->flags & KSF_CLONED) {
+ ng_send_fn(node, NULL, &ng_ksocket_incoming2, so, M_NOWAIT);
+ }
- /* OK */
- priv->hook = hook;
return (0);
}
@@ -572,6 +662,7 @@ ng_ksocket_rcvmsg(node_p node, item_p item, hook_p lasthook)
struct ng_mesg *resp = NULL;
int error = 0;
struct ng_mesg *msg;
+ ng_ID_t raddr;
NGI_GET_MSG(item, msg);
switch (msg->header.typecookie) {
@@ -596,18 +687,13 @@ ng_ksocket_rcvmsg(node_p node, item_p item, hook_p lasthook)
case NGM_KSOCKET_LISTEN:
{
/* Sanity check */
- if (msg->header.arglen != sizeof(int))
+ if (msg->header.arglen != sizeof(int32_t))
ERROUT(EINVAL);
if (so == NULL)
ERROUT(ENXIO);
/* Listen */
- if ((error = solisten(so, *((int *)msg->data), p)) != 0)
- break;
-
- /* Notify sender when we get a connection attempt */
- /* XXX implement me */
- error = ENODEV;
+ error = solisten(so, *((int32_t *)msg->data), p);
break;
}
@@ -619,14 +705,27 @@ ng_ksocket_rcvmsg(node_p node, item_p item, hook_p lasthook)
if (so == NULL)
ERROUT(ENXIO);
- /* Accept on the socket in a non-blocking way */
- /* Create a new ksocket node for the new connection */
- /* Return a response with the peer's sockaddr and
- the absolute name of the newly created node */
-
- /* XXX implement me */
+ /* Make sure the socket is capable of accepting */
+ if (!(so->so_options & SO_ACCEPTCONN))
+ ERROUT(EINVAL);
+ if (priv->flags & KSF_ACCEPTING)
+ ERROUT(EALREADY);
+
+ error = ng_ksocket_check_accept(priv);
+ if (error != 0 && error != EWOULDBLOCK)
+ ERROUT(error);
- error = ENODEV;
+ /*
+ * If a connection is already complete, take it.
+ * Otherwise let the upcall function deal with
+ * the connection when it comes in.
+ */
+ priv->response_token = msg->header.token;
+ raddr = priv->response_addr;
+ if (error == 0) {
+ ng_ksocket_finish_accept(priv);
+ } else
+ priv->flags |= KSF_ACCEPTING;
break;
}
@@ -650,8 +749,10 @@ ng_ksocket_rcvmsg(node_p node, item_p item, hook_p lasthook)
ERROUT(error);
}
if ((so->so_state & SS_ISCONNECTING) != 0)
- /* Notify sender when we connect */
- /* XXX implement me */
+ /* We will notify the sender when we connect */
+ priv->response_token = msg->header.token;
+ raddr = priv->response_addr;
+ priv->flags |= KSF_CONNECTING;
ERROUT(EINPROGRESS);
break;
}
@@ -720,7 +821,7 @@ ng_ksocket_rcvmsg(node_p node, item_p item, hook_p lasthook)
sopt.sopt_dir = SOPT_GET;
sopt.sopt_level = ksopt->level;
sopt.sopt_name = ksopt->name;
- sopt.sopt_p = p;
+ sopt.sopt_p = NULL;
sopt.sopt_valsize = NG_KSOCKET_MAX_OPTLEN;
ksopt = (struct ng_ksocket_sockopt *)resp->data;
sopt.sopt_val = ksopt->value;
@@ -754,7 +855,7 @@ ng_ksocket_rcvmsg(node_p node, item_p item, hook_p lasthook)
sopt.sopt_name = ksopt->name;
sopt.sopt_val = ksopt->value;
sopt.sopt_valsize = valsize;
- sopt.sopt_p = p;
+ sopt.sopt_p = NULL;
error = sosetopt(so, &sopt);
break;
}
@@ -800,16 +901,29 @@ static int
ng_ksocket_shutdown(node_p node)
{
const priv_p priv = NG_NODE_PRIVATE(node);
+ priv_p embryo;
/* Close our socket (if any) */
if (priv->so != NULL) {
-
priv->so->so_upcall = NULL;
priv->so->so_rcv.sb_flags &= ~SB_UPCALL;
+ priv->so->so_snd.sb_flags &= ~SB_UPCALL;
soclose(priv->so);
priv->so = NULL;
}
+ /* If we are an embryo, take ourselves out of the parent's list */
+ if (priv->flags & KSF_EMBRYONIC) {
+ LIST_REMOVE(priv, siblings);
+ priv->flags &= ~KSF_EMBRYONIC;
+ }
+
+ /* Remove any embryonic children we have */
+ while (!LIST_EMPTY(&priv->embryos)) {
+ embryo = LIST_FIRST(&priv->embryos);
+ ng_rmnode_self(embryo->node);
+ }
+
/* Take down netgraph node */
bzero(priv, sizeof(*priv));
FREE(priv, M_NETGRAPH_KSOCKET);
@@ -835,16 +949,52 @@ ng_ksocket_disconnect(hook_p hook)
/************************************************************************
HELPER STUFF
************************************************************************/
+/*
+ * You should no-longer "just call" a netgraph node function
+ * from an external asynchronous event.
+ * This is because in doing so you are ignoring the locking on the netgraph
+ * nodes. Instead call your function via
+ * "int ng_send_fn(node_p node, hook_p hook, ng_item_fn *fn,
+ * void *arg1, int arg2);"
+ * this will call the function you chose, but will first do all the
+ * locking rigmarole. Your function MAY only be called at some distant future
+ * time (several millisecs away) so don't give it any arguments
+ * that may be revoked soon (e.g. on your stack).
+ * In this case even the 'so' argument is doubtful.
+ * While the function request is being processed the node
+ * has an extra reference and as such will not disappear until
+ * the request has at least been done, but the 'so' may not be so lucky.
+ * handle this by checking the validity of the node in the target function
+ * before dereferencing the socket pointer.
+ */
+
+static void
+ng_ksocket_incoming(struct socket *so, void *arg, int waitflag)
+{
+ const node_p node = arg;
+
+ ng_send_fn(node, NULL, &ng_ksocket_incoming2, so, waitflag);
+}
+
/*
* When incoming data is appended to the socket, we get notified here.
+ * This is also called whenever a significant event occurs for the socket.
+ * We know that HOOK is NULL. Because of how we were called we know we have a
+ * lock on this node an are participating inthe netgraph locking.
+ * Our original caller may have queued this even some time ago and
+ * we cannot trust that he even still exists. The node however is being
+ * held with a reference by the queueing code, at least until we finish,
+ * even if it has been zapped, so first check it's validiy
+ * before we trust the socket (which was derived from it).
*/
static void
-ng_ksocket_incoming(struct socket *so, void *arg, int waitflag)
+ng_ksocket_incoming2(node_p node, hook_p hook, void *arg1, int waitflag)
{
- const node_p node = arg;
+ struct socket *so = arg1;
const priv_p priv = NG_NODE_PRIVATE(node);
struct mbuf *m;
+ struct ng_mesg *response;
struct uio auio;
int s, flags, error;
@@ -855,8 +1005,52 @@ ng_ksocket_incoming(struct socket *so, void *arg, int waitflag)
splx(s);
return;
}
+ /* so = priv->so; *//* XXX could have derived this like so */
KASSERT(so == priv->so, ("%s: wrong socket", __FUNCTION__));
- KASSERT(priv->hook != NULL, ("%s: no hook", __FUNCTION__));
+
+ /* Check whether a pending connect operation has completed */
+ if (priv->flags & KSF_CONNECTING) {
+ if ((error = so->so_error) != 0) {
+ so->so_error = 0;
+ so->so_state &= ~SS_ISCONNECTING;
+ }
+ if (!(so->so_state & SS_ISCONNECTING)) {
+ NG_MKMESSAGE(response, NGM_KSOCKET_COOKIE,
+ NGM_KSOCKET_CONNECT, sizeof(int32_t), waitflag);
+ if (response != NULL) {
+ response->header.flags |= NGF_RESP;
+ response->header.token = priv->response_token;
+ *(int32_t *)response->data = error;
+ /*
+ * send an async "response" message
+ * to the node that set us up
+ * (if it still exists)
+ */
+ NG_SEND_MSG_ID(error, node, response,
+ priv->response_addr, NULL);
+ }
+ priv->flags &= ~KSF_CONNECTING;
+ }
+ }
+
+ /* Check whether a pending accept operation has completed */
+ if (priv->flags & KSF_ACCEPTING) {
+ error = ng_ksocket_check_accept(priv);
+ if (error != EWOULDBLOCK)
+ priv->flags &= ~KSF_ACCEPTING;
+ if (error == 0)
+ ng_ksocket_finish_accept(priv);
+ }
+
+ /*
+ * If we don't have a hook, we must handle data events later. When
+ * the hook gets created and is connected, this upcall function
+ * will be called again.
+ */
+ if (priv->hook == NULL) {
+ splx(s);
+ return;
+ }
/* Read and forward available mbuf's */
auio.uio_procp = NULL;
@@ -876,10 +1070,133 @@ ng_ksocket_incoming(struct socket *so, void *arg, int waitflag)
NG_SEND_DATA_ONLY(error, priv->hook, m);
}
} while (error == 0 && m != NULL);
+
+ /*
+ * If the peer has closed the connection, forward a 0-length mbuf
+ * to indicate end-of-file.
+ */
+ if (so->so_state & SS_CANTRCVMORE && !(priv->flags & KSF_EOFSEEN)) {
+ MGETHDR(m, waitflag, MT_DATA);
+ if (m != NULL) {
+ m->m_len = m->m_pkthdr.len = 0;
+ NG_SEND_DATA_ONLY(error, priv->hook, m);
+ }
+ priv->flags |= KSF_EOFSEEN;
+ }
splx(s);
}
/*
+ * Check for a completed incoming connection and return 0 if one is found.
+ * Otherwise return the appropriate error code.
+ */
+static int
+ng_ksocket_check_accept(priv_p priv)
+{
+ struct socket *const head = priv->so;
+ int error;
+
+ if ((error = head->so_error) != 0) {
+ head->so_error = 0;
+ return error;
+ }
+ if (TAILQ_EMPTY(&head->so_comp)) {
+ if (head->so_state & SS_CANTRCVMORE)
+ return ECONNABORTED;
+ return EWOULDBLOCK;
+ }
+ return 0;
+}
+
+/*
+ * Handle the first completed incoming connection, assumed to be already
+ * on the socket's so_comp queue.
+ */
+static void
+ng_ksocket_finish_accept(priv_p priv)
+{
+ struct socket *const head = priv->so;
+ struct socket *so;
+ struct sockaddr *sa = NULL;
+ struct ng_mesg *resp;
+ struct ng_ksocket_accept *resp_data;
+ node_p node;
+ priv_p priv2;
+ int len;
+ int error;
+
+ so = TAILQ_FIRST(&head->so_comp);
+ if (so == NULL) /* Should never happen */
+ return;
+ TAILQ_REMOVE(&head->so_comp, so, so_list);
+ head->so_qlen--;
+
+ /* XXX KNOTE(&head->so_rcv.sb_sel.si_note, 0); */
+
+ so->so_state &= ~SS_COMP;
+ so->so_state |= SS_NBIO;
+ so->so_head = NULL;
+
+ soaccept(so, &sa);
+
+ len = OFFSETOF(struct ng_ksocket_accept, addr);
+ if (sa != NULL)
+ len += sa->sa_len;
+
+ NG_MKMESSAGE(resp, NGM_KSOCKET_COOKIE, NGM_KSOCKET_ACCEPT, len,
+ M_NOWAIT);
+ if (resp == NULL) {
+ soclose(so);
+ goto out;
+ }
+ resp->header.flags |= NGF_RESP;
+ resp->header.token = priv->response_token;
+
+ /* Clone a ksocket node to wrap the new socket */
+ error = ng_make_node_common(&ng_ksocket_typestruct, &node);
+ if (error) {
+ FREE(resp, M_NETGRAPH);
+ soclose(so);
+ goto out;
+ }
+
+ if (ng_ksocket_constructor(node) != 0) {
+ NG_NODE_UNREF(node);
+ FREE(resp, M_NETGRAPH);
+ soclose(so);
+ goto out;
+ }
+
+ priv2 = NG_NODE_PRIVATE(node);
+ priv2->so = so;
+ priv2->flags |= KSF_CLONED | KSF_EMBRYONIC;
+
+ /*
+ * Insert the cloned node into a list of embryonic children
+ * on the parent node. When a hook is created on the cloned
+ * node it will be removed from this list. When the parent
+ * is destroyed it will destroy any embryonic children it has.
+ */
+ LIST_INSERT_HEAD(&priv->embryos, priv2, siblings);
+
+ so->so_upcallarg = (caddr_t)node;
+ so->so_upcall = ng_ksocket_incoming;
+ so->so_rcv.sb_flags |= SB_UPCALL;
+ so->so_snd.sb_flags |= SB_UPCALL;
+
+ /* Fill in the response data and send it or return it to the caller */
+ resp_data = (struct ng_ksocket_accept *)resp->data;
+ resp_data->nodeid = NG_NODE_ID(node);
+ if (sa != NULL)
+ bcopy(sa, &resp_data->addr, sa->sa_len);
+ NG_SEND_MSG_ID(error, node, resp, priv->response_addr, NULL);
+
+out:
+ if (sa != NULL)
+ FREE(sa, M_SONAME);
+}
+
+/*
* Parse out either an integer value or an alias.
*/
static int
diff --git a/sys/netgraph/ng_ksocket.h b/sys/netgraph/ng_ksocket.h
index fd6a579..efb65aa 100644
--- a/sys/netgraph/ng_ksocket.h
+++ b/sys/netgraph/ng_ksocket.h
@@ -43,6 +43,8 @@
#ifndef _NETGRAPH_KSOCKET_H_
#define _NETGRAPH_KSOCKET_H_
+#include <sys/socket.h>
+
/* Node type name and magic cookie */
#define NG_KSOCKET_NODE_TYPE "ksocket"
#define NGM_KSOCKET_COOKIE 942710669
@@ -69,6 +71,21 @@ struct ng_ksocket_sockopt {
} \
}
+/* For NGM_KSOCKET_ACCEPT control message responses */
+struct ng_ksocket_accept {
+ u_int32_t nodeid; /* node ID of connected ksocket */
+ struct sockaddr addr; /* peer's address (variable length) */
+};
+
+/* Keep this in sync with the above structure definition */
+#define NGM_KSOCKET_ACCEPT_INFO { \
+ { \
+ { "nodeid", &ng_parse_hint32_type }, \
+ { "addr", &ng_ksocket_generic_sockaddr_type }, \
+ { NULL } \
+ } \
+}
+
/* Netgraph commands */
enum {
NGM_KSOCKET_BIND = 1,
OpenPOWER on IntegriCloud