示例#1
0
/* This will need only if native IP used, or (unlikely) NCP will be
 * implemented on the socket level
 */
static int
ncp_soconnect(struct socket *so,struct sockaddr *target, struct thread *td) {
	int error;

	error = soconnect(so, (struct sockaddr*)target, td);
	if (error)
		return error;
	/*
	 * Wait for the connection to complete. Cribbed from the
	 * connect system call but with the wait timing out so
	 * that interruptible mounts don't hang here for a long time.
	 */
	error = EIO;
	crit_enter();
	while ((so->so_state & SS_ISCONNECTING) && so->so_error == 0) {
		tsleep((caddr_t)&so->so_timeo, 0, "ncpcon", 2 * hz);
		if ((so->so_state & SS_ISCONNECTING) &&
		    so->so_error == 0 /*&& rep &&*/) {
			soclrstate(so, SS_ISCONNECTING);
			crit_exit();
			goto bad;
		}
	}
	if (so->so_error) {
		error = so->so_error;
		so->so_error = 0;
		crit_exit();
		goto bad;
	}
	crit_exit();
	error=0;
bad:
	return error;
}
示例#2
0
/*
 * Destroy a disconnected socket.  This routine is a NOP if entities
 * still have a reference on the socket:
 *
 *	so_pcb -	The protocol stack still has a reference
 *	SS_NOFDREF -	There is no longer a file pointer reference
 */
void
sofree(struct socket *so)
{
	struct socket *head;

	/*
	 * This is a bit hackish at the moment.  We need to interlock
	 * any accept queue we are on before we potentially lose the
	 * last reference to avoid races against a re-reference from
	 * someone operating on the queue.
	 */
	while ((head = so->so_head) != NULL) {
		lwkt_getpooltoken(head);
		if (so->so_head == head)
			break;
		lwkt_relpooltoken(head);
	}

	/*
	 * Arbitrage the last free.
	 */
	KKASSERT(so->so_refs > 0);
	if (atomic_fetchadd_int(&so->so_refs, -1) != 1) {
		if (head)
			lwkt_relpooltoken(head);
		return;
	}

	KKASSERT(so->so_pcb == NULL && (so->so_state & SS_NOFDREF));
	KKASSERT((so->so_state & SS_ASSERTINPROG) == 0);

	/*
	 * We're done, remove ourselves from the accept queue we are
	 * on, if we are on one.
	 */
	if (head != NULL) {
		if (so->so_state & SS_INCOMP) {
			TAILQ_REMOVE(&head->so_incomp, so, so_list);
			head->so_incqlen--;
		} else if (so->so_state & SS_COMP) {
			/*
			 * We must not decommission a socket that's
			 * on the accept(2) queue.  If we do, then
			 * accept(2) may hang after select(2) indicated
			 * that the listening socket was ready.
			 */
			lwkt_relpooltoken(head);
			return;
		} else {
			panic("sofree: not queued");
		}
		soclrstate(so, SS_INCOMP);
		so->so_head = NULL;
		lwkt_relpooltoken(head);
	}
	ssb_release(&so->so_snd, so);
	sorflush(so);
	sodealloc(so);
}
示例#3
0
/*
 * so is passed in ref'd, which becomes owned by
 * the cleared SS_NOFDREF flag.
 */
int
soaccept(struct socket *so, struct sockaddr **nam)
{
	int error;

	if ((so->so_state & SS_NOFDREF) == 0)
		panic("soaccept: !NOFDREF");
	soclrstate(so, SS_NOFDREF);	/* owned by lack of SS_NOFDREF */
	error = so_pru_accept_direct(so, nam);
	return (error);
}
示例#4
0
/*
 * If only one hook, allow read(2) and write(2) to work.
 */
static int
ngs_connect(hook_p hook)
{
	node_p node = NG_HOOK_NODE(hook);
	struct ngsock *priv = NG_NODE_PRIVATE(node);

	if ((priv->datasock) && (priv->datasock->ng_socket)) {
		if (NG_NODE_NUMHOOKS(node) == 1)
			sosetstate(priv->datasock->ng_socket, SS_ISCONNECTED);
		else
			soclrstate(priv->datasock->ng_socket, SS_ISCONNECTED);
	}
	return (0);
}
示例#5
0
/*
 * Hook disconnection
 *
 * For this type, removal of the last link destroys the node
 * if the NOLINGER flag is set.
 */
static int
ngs_disconnect(hook_p hook)
{
	node_p node = NG_HOOK_NODE(hook);
	struct ngsock *const priv = NG_NODE_PRIVATE(node);

	if ((priv->datasock) && (priv->datasock->ng_socket)) {
		if (NG_NODE_NUMHOOKS(node) == 1)
			sosetstate(priv->datasock->ng_socket, SS_ISCONNECTED);
		else
			soclrstate(priv->datasock->ng_socket, SS_ISCONNECTED);
	}

	if ((priv->flags & NGS_FLAG_NOLINGER) &&
	    (NG_NODE_NUMHOOKS(node) == 0) && (NG_NODE_IS_VALID(node)))
		ng_rmnode_self(node);

	return (0);
}
示例#6
0
static void
udp6_disconnect(netmsg_t msg)
{
	struct socket *so = msg->disconnect.base.nm_so;
	struct inpcb *inp;
	int error;

	inp = so->so_pcb;
	if (inp == NULL) {
		error = EINVAL;
		goto out;
	}

	if (IN6_IS_ADDR_UNSPECIFIED(&inp->in6p_faddr)) {
		error = ENOTCONN;
	} else {
		in6_pcbdisconnect(inp);
		soclrstate(so, SS_ISCONNECTED);		/* XXX */
		error = 0;
	}
out:
	lwkt_replymsg(&msg->disconnect.base.lmsg, error);
}
示例#7
0
/*
 * MPSAFE
 */
int
soo_ioctl(struct file *fp, u_long cmd, caddr_t data,
	  struct ucred *cred, struct sysmsg *msg)
{
	struct socket *so;
	int error;

	so = (struct socket *)fp->f_data;

	switch (cmd) {
	case FIOASYNC:
		if (*(int *)data) {
			sosetstate(so, SS_ASYNC);
			atomic_set_int(&so->so_rcv.ssb_flags,  SSB_ASYNC);
			atomic_set_int(&so->so_snd.ssb_flags, SSB_ASYNC);
		} else {
			soclrstate(so, SS_ASYNC);
			atomic_clear_int(&so->so_rcv.ssb_flags, SSB_ASYNC);
			atomic_clear_int(&so->so_snd.ssb_flags, SSB_ASYNC);
		}
		error = 0;
		break;
	case FIONREAD:
		*(int *)data = so->so_rcv.ssb_cc;
		error = 0;
		break;
	case FIOSETOWN:
		error = fsetown(*(int *)data, &so->so_sigio);
		break;
	case FIOGETOWN:
		*(int *)data = fgetown(&so->so_sigio);
		error = 0;
		break;
	case SIOCSPGRP:
		error = fsetown(-(*(int *)data), &so->so_sigio);
		break;
	case SIOCGPGRP:
		*(int *)data = -fgetown(&so->so_sigio);
		error = 0;
		break;
	case SIOCATMARK:
		*(int *)data = (so->so_state&SS_RCVATMARK) != 0;
		error = 0;
		break;
	default:
		/*
		 * Interface/routing/protocol specific ioctls:
		 * interface and routing ioctls should have a
		 * different entry since a socket's unnecessary
		 */
		if (IOCGROUP(cmd) == 'i') {
			error = ifioctl(so, cmd, data, cred);
		} else if (IOCGROUP(cmd) == 'r') {
			error = rtioctl(cmd, data, cred);
		} else {
			error = so_pru_control_direct(so, cmd, data, NULL);
		}
		break;
	}
	return (error);
}
示例#8
0
/*
 * Receive a control message
 */
static int
ng_ksocket_rcvmsg(node_p node, item_p item, hook_p lasthook)
{
	struct thread *td = curthread;	/* XXX broken */
	const priv_p priv = NG_NODE_PRIVATE(node);
	struct socket *const so = priv->so;
	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) {
	case NGM_KSOCKET_COOKIE:
		switch (msg->header.cmd) {
		case NGM_KSOCKET_BIND:
		    {
			struct sockaddr *const sa
			    = (struct sockaddr *)msg->data;

			/* Sanity check */
			if (msg->header.arglen < SADATA_OFFSET
			    || msg->header.arglen < sa->sa_len)
				ERROUT(EINVAL);
			if (so == NULL)
				ERROUT(ENXIO);

			/* Bind */
			error = sobind(so, sa, td);
			break;
		    }
		case NGM_KSOCKET_LISTEN:
		    {
			/* Sanity check */
			if (msg->header.arglen != sizeof(int32_t))
				ERROUT(EINVAL);
			if (so == NULL)
				ERROUT(ENXIO);

			/* Listen */
			error = solisten(so, *((int32_t *)msg->data), td);
			break;
		    }

		case NGM_KSOCKET_ACCEPT:
		    {
			/* Sanity check */
			if (msg->header.arglen != 0)
				ERROUT(EINVAL);
			if (so == NULL)
				ERROUT(ENXIO);

			/* 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);

			/*
			 * 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 = NGI_RETADDR(item);
			if (error == 0) {
				ng_ksocket_finish_accept(priv);
			} else
				priv->flags |= KSF_ACCEPTING;
			break;
		    }

		case NGM_KSOCKET_CONNECT:
		    {
			struct sockaddr *const sa
			    = (struct sockaddr *)msg->data;

			/* Sanity check */
			if (msg->header.arglen < SADATA_OFFSET
			    || msg->header.arglen < sa->sa_len)
				ERROUT(EINVAL);
			if (so == NULL)
				ERROUT(ENXIO);

			/* Do connect */
			if ((so->so_state & SS_ISCONNECTING) != 0)
				ERROUT(EALREADY);
			if ((error = soconnect(so, sa, td)) != 0) {
				soclrstate(so, SS_ISCONNECTING);
				ERROUT(error);
			}
			if ((so->so_state & SS_ISCONNECTING) != 0) {
				/* We will notify the sender when we connect */
				priv->response_token = msg->header.token;
				raddr = priv->response_addr = NGI_RETADDR(item);
				priv->flags |= KSF_CONNECTING;
				ERROUT(EINPROGRESS);
			}
			break;
		    }

		case NGM_KSOCKET_GETNAME:
		case NGM_KSOCKET_GETPEERNAME:
		    {
			int (*func)(struct socket *so, struct sockaddr **nam);
			struct sockaddr *sa = NULL;
			int len;

			/* Sanity check */
			if (msg->header.arglen != 0)
				ERROUT(EINVAL);
			if (so == NULL)
				ERROUT(ENXIO);

			/* Get function */
			if (msg->header.cmd == NGM_KSOCKET_GETPEERNAME) {
				if ((so->so_state
				    & (SS_ISCONNECTED|SS_ISCONFIRMING)) == 0) 
					ERROUT(ENOTCONN);
				func = so->so_proto->pr_usrreqs->pru_peeraddr;
			} else
				func = so->so_proto->pr_usrreqs->pru_sockaddr;

			/* Get local or peer address */
			if ((error = (*func)(so, &sa)) != 0)
				goto bail;
			len = (sa == NULL) ? 0 : sa->sa_len;

			/* Send it back in a response */
			NG_MKRESPONSE(resp, msg, len, M_WAITOK | M_NULLOK);
			if (resp == NULL) {
				error = ENOMEM;
				goto bail;
			}
			bcopy(sa, resp->data, len);

		bail:
			/* Cleanup */
			if (sa != NULL)
				kfree(sa, M_SONAME);
			break;
		    }

		case NGM_KSOCKET_GETOPT:
		    {
			struct ng_ksocket_sockopt *ksopt = 
			    (struct ng_ksocket_sockopt *)msg->data;
			struct sockopt sopt;

			/* Sanity check */
			if (msg->header.arglen != sizeof(*ksopt))
				ERROUT(EINVAL);
			if (so == NULL)
				ERROUT(ENXIO);

			/* Get response with room for option value */
			NG_MKRESPONSE(resp, msg, sizeof(*ksopt)
			    + NG_KSOCKET_MAX_OPTLEN, M_WAITOK | M_NULLOK);
			if (resp == NULL)
				ERROUT(ENOMEM);

			/* Get socket option, and put value in the response */
			sopt.sopt_dir = SOPT_GET;
			sopt.sopt_level = ksopt->level;
			sopt.sopt_name = ksopt->name;
			sopt.sopt_td = NULL;
			sopt.sopt_valsize = NG_KSOCKET_MAX_OPTLEN;
			ksopt = (struct ng_ksocket_sockopt *)resp->data;
			sopt.sopt_val = ksopt->value;
			if ((error = sogetopt(so, &sopt)) != 0) {
				NG_FREE_MSG(resp);
				break;
			}

			/* Set actual value length */
			resp->header.arglen = sizeof(*ksopt)
			    + sopt.sopt_valsize;
			break;
		    }

		case NGM_KSOCKET_SETOPT:
		    {
			struct ng_ksocket_sockopt *const ksopt = 
			    (struct ng_ksocket_sockopt *)msg->data;
			const int valsize = msg->header.arglen - sizeof(*ksopt);
			struct sockopt sopt;

			/* Sanity check */
			if (valsize < 0)
				ERROUT(EINVAL);
			if (so == NULL)
				ERROUT(ENXIO);

			/* Set socket option */
			sopt.sopt_dir = SOPT_SET;
			sopt.sopt_level = ksopt->level;
			sopt.sopt_name = ksopt->name;
			sopt.sopt_val = ksopt->value;
			sopt.sopt_valsize = valsize;
			sopt.sopt_td = NULL;
			error = sosetopt(so, &sopt);
			break;
		    }

		default:
			error = EINVAL;
			break;
		}
		break;
	default:
		error = EINVAL;
		break;
	}
done:
	NG_RESPOND_MSG(error, node, item, resp);
	NG_FREE_MSG(msg);
	return (error);
}
示例#9
0
/*
 * When incoming data is appended to the socket, we get notified here.
 * This is also called whenever a significant event occurs for the socket.
 * 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 and guarantied to be valid.
 */
static void
ng_ksocket_incoming2(node_p node, hook_p hook, void *arg1, int arg2)
{
	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;

	s = splnet();

	/* so = priv->so; *//* XXX could have derived this like so */
	KASSERT(so == priv->so, ("%s: wrong socket", __func__));
	
	/* Allow next incoming event to be queued. */
	atomic_store_rel_int(&priv->fn_sent, 0);

	/* Check whether a pending connect operation has completed */
	if (priv->flags & KSF_CONNECTING) {
		if ((error = so->so_error) != 0) {
			so->so_error = 0;
			soclrstate(so, SS_ISCONNECTING);
		}
		if (!(so->so_state & SS_ISCONNECTING)) {
			NG_MKMESSAGE(response, NGM_KSOCKET_COOKIE,
			    NGM_KSOCKET_CONNECT, sizeof(int32_t), M_WAITOK | M_NULLOK);
			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, 0);
			}
			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_td = NULL;
	auio.uio_resid = 1000000000;
	flags = MSG_DONTWAIT;
	while (1) {
		struct sockaddr *sa = NULL;
		struct mbuf *n;

		/* Try to get next packet from socket */
		if ((error = soreceive(so, (so->so_state & SS_ISCONNECTED) ?
		    NULL : &sa, &auio, &m, NULL, &flags)) != 0)
			break;

		/* See if we got anything */
		if (m == NULL) {
			if (sa != NULL)
				kfree(sa, M_SONAME);
			break;
		}

		/*
		 * Don't trust the various socket layers to get the
		 * packet header and length correct (e.g. kern/15175).
		 *
		 * Also, do not trust that soreceive() will clear m_nextpkt
		 * for us (e.g. kern/84952, kern/82413).
		 */
		m->m_pkthdr.csum_flags = 0;
		for (n = m, m->m_pkthdr.len = 0; n != NULL; n = n->m_next) {
			m->m_pkthdr.len += n->m_len;
			n->m_nextpkt = NULL;
		}

		/* Put peer's socket address (if any) into a tag */
		if (sa != NULL) {
			struct sa_tag	*stag;

			stag = (struct sa_tag *)m_tag_alloc(NGM_KSOCKET_COOKIE,
			    NG_KSOCKET_TAG_SOCKADDR, sizeof(ng_ID_t) +
			    sa->sa_len, MB_DONTWAIT);
			if (stag == NULL) {
				kfree(sa, M_SONAME);
				goto sendit;
			}
			bcopy(sa, &stag->sa, sa->sa_len);
			kfree(sa, M_SONAME);
			stag->id = NG_NODE_ID(node);
			m_tag_prepend(m, &stag->tag);
		}

sendit:		/* Forward data with optional peer sockaddr as packet tag */
		NG_SEND_DATA_ONLY(error, priv->hook, m);
	}

	/*
	 * If the peer has closed the connection, forward a 0-length mbuf
	 * to indicate end-of-file.
	 */
	if (so->so_rcv.sb_state & SBS_CANTRCVMORE && !(priv->flags & KSF_EOFSEEN)) {
		MGETHDR(m, MB_DONTWAIT, 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);
}
示例#10
0
/*
 * Close a socket on last file table reference removal.
 * Initiate disconnect if connected.
 * Free socket when disconnect complete.
 */
int
soclose(struct socket *so, int fflag)
{
	int error = 0;

	funsetown(&so->so_sigio);
	if (so->so_pcb == NULL)
		goto discard;
	if (so->so_state & SS_ISCONNECTED) {
		if ((so->so_state & SS_ISDISCONNECTING) == 0) {
			error = sodisconnect(so);
			if (error)
				goto drop;
		}
		if (so->so_options & SO_LINGER) {
			if ((so->so_state & SS_ISDISCONNECTING) &&
			    (fflag & FNONBLOCK))
				goto drop;
			while (so->so_state & SS_ISCONNECTED) {
				error = tsleep(&so->so_timeo, PCATCH,
					       "soclos", so->so_linger * hz);
				if (error)
					break;
			}
		}
	}
drop:
	if (so->so_pcb) {
		int error2;

		error2 = so_pru_detach(so);
		if (error == 0)
			error = error2;
	}
discard:
	lwkt_getpooltoken(so);
	if (so->so_options & SO_ACCEPTCONN) {
		struct socket *sp;

		while ((sp = TAILQ_FIRST(&so->so_incomp)) != NULL) {
			TAILQ_REMOVE(&so->so_incomp, sp, so_list);
			soclrstate(sp, SS_INCOMP);
			sp->so_head = NULL;
			so->so_incqlen--;
			soaborta(sp);
		}
		while ((sp = TAILQ_FIRST(&so->so_comp)) != NULL) {
			TAILQ_REMOVE(&so->so_comp, sp, so_list);
			soclrstate(sp, SS_COMP);
			sp->so_head = NULL;
			so->so_qlen--;
			soaborta(sp);
		}
	}
	lwkt_relpooltoken(so);
	if (so->so_state & SS_NOFDREF)
		panic("soclose: NOFDREF");
	sosetstate(so, SS_NOFDREF);	/* take ref */
	sofree(so);			/* dispose of ref */
	return (error);
}
示例#11
0
int
socreate(int dom, struct socket **aso, int type,
	int proto, struct thread *td)
{
	struct proc *p = td->td_proc;
	struct protosw *prp;
	struct socket *so;
	struct pru_attach_info ai;
	int error;

	if (proto)
		prp = pffindproto(dom, proto, type);
	else
		prp = pffindtype(dom, type);

	if (prp == 0 || prp->pr_usrreqs->pru_attach == 0)
		return (EPROTONOSUPPORT);

	if (p->p_ucred->cr_prison && jail_socket_unixiproute_only &&
	    prp->pr_domain->dom_family != PF_LOCAL &&
	    prp->pr_domain->dom_family != PF_INET &&
	    prp->pr_domain->dom_family != PF_INET6 &&
	    prp->pr_domain->dom_family != PF_ROUTE) {
		return (EPROTONOSUPPORT);
	}

	if (prp->pr_type != type)
		return (EPROTOTYPE);
	so = soalloc(p != 0);
	if (so == NULL)
		return (ENOBUFS);

	/*
	 * Callers of socreate() presumably will connect up a descriptor
	 * and call soclose() if they cannot.  This represents our so_refs
	 * (which should be 1) from soalloc().
	 */
	soclrstate(so, SS_NOFDREF);

	/*
	 * Set a default port for protocol processing.  No action will occur
	 * on the socket on this port until an inpcb is attached to it and
	 * is able to match incoming packets, or until the socket becomes
	 * available to userland.
	 *
	 * We normally default the socket to the protocol thread on cpu 0.
	 * If PR_SYNC_PORT is set (unix domain sockets) there is no protocol
	 * thread and all pr_*()/pru_*() calls are executed synchronously.
	 */
	if (prp->pr_flags & PR_SYNC_PORT)
		so->so_port = &netisr_sync_port;
	else
		so->so_port = cpu_portfn(0);

	TAILQ_INIT(&so->so_incomp);
	TAILQ_INIT(&so->so_comp);
	so->so_type = type;
	so->so_cred = crhold(p->p_ucred);
	so->so_proto = prp;
	ai.sb_rlimit = &p->p_rlimit[RLIMIT_SBSIZE];
	ai.p_ucred = p->p_ucred;
	ai.fd_rdir = p->p_fd->fd_rdir;

	/*
	 * Auto-sizing of socket buffers is managed by the protocols and
	 * the appropriate flags must be set in the pru_attach function.
	 */
	error = so_pru_attach(so, proto, &ai);
	if (error) {
		sosetstate(so, SS_NOFDREF);
		sofree(so);	/* from soalloc */
		return error;
	}

	/*
	 * NOTE: Returns referenced socket.
	 */
	*aso = so;
	return (0);
}
示例#12
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;

	lwkt_getpooltoken(head);
	so = TAILQ_FIRST(&head->so_comp);
	if (so == NULL) {	/* Should never happen */
		lwkt_relpooltoken(head);
		return;
	}
	TAILQ_REMOVE(&head->so_comp, so, so_list);
	head->so_qlen--;
	soclrstate(so, SS_COMP);
	so->so_head = NULL;
	soreference(so);
	lwkt_relpooltoken(head);

	/* XXX KNOTE(&head->so_rcv.ssb_sel.si_note, 0); */

	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_WAITOK | M_NULLOK);
	if (resp == NULL) {
		soclose(so, FNONBLOCK);
		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) {
		kfree(resp, M_NETGRAPH);
		soclose(so, FNONBLOCK);
		goto out;
	}

	if (ng_ksocket_constructor(node) != 0) {
		NG_NODE_UNREF(node);
		kfree(resp, M_NETGRAPH);
		soclose(so, FNONBLOCK);
		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;
	atomic_set_int(&priv->so->so_rcv.ssb_flags, SSB_UPCALL);
	atomic_set_int(&priv->so->so_snd.ssb_flags, SSB_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, 0);

out:
	if (sa != NULL)
		kfree(sa, M_SONAME);
}