/* * Mark the connection as being incapable of further output. */ static int tcp_usr_shutdown(struct socket *so) { int error = 0; struct inpcb *inp; struct tcpcb *tp = NULL; TCPDEBUG0; INP_INFO_WLOCK(&V_tcbinfo); inp = sotoinpcb(so); KASSERT(inp != NULL, ("inp == NULL")); INP_WLOCK(inp); if (inp->inp_flags & (INP_TIMEWAIT | INP_DROPPED)) { error = ECONNRESET; goto out; } tp = intotcpcb(inp); TCPDEBUG1(); socantsendmore(so); tcp_usrclosed(tp); if (!(inp->inp_flags & INP_DROPPED)) error = tcp_output_disconnect(tp); out: TCPDEBUG2(PRU_SHUTDOWN); INP_WUNLOCK(inp); INP_INFO_WUNLOCK(&V_tcbinfo); return (error); }
/* * Initiate (or continue) disconnect. * If embryonic state, just send reset (once). * If in ``let data drain'' option and linger null, just drop. * Otherwise (hard), mark socket disconnecting and drop * current input data; switch states based on user close, and * send segment to peer (with FIN). */ static void tcp_disconnect(struct tcpcb *tp) { struct inpcb *inp = tp->t_inpcb; struct socket *so = inp->inp_socket; INP_INFO_WLOCK_ASSERT(&V_tcbinfo); INP_WLOCK_ASSERT(inp); /* * Neither tcp_close() nor tcp_drop() should return NULL, as the * socket is still open. */ if (tp->t_state < TCPS_ESTABLISHED) { tp = tcp_close(tp); KASSERT(tp != NULL, ("tcp_disconnect: tcp_close() returned NULL")); } else if ((so->so_options & SO_LINGER) && so->so_linger == 0) { tp = tcp_drop(tp, 0); KASSERT(tp != NULL, ("tcp_disconnect: tcp_drop() returned NULL")); } else { soisdisconnecting(so); sbflush(&so->so_rcv); tcp_usrclosed(tp); if (!(inp->inp_flags & INP_DROPPED)) tcp_output_disconnect(tp); } }
/* * Mark the connection as being incapable of further output. */ static void tcp_usr_shutdown(netmsg_t msg) { struct socket *so = msg->shutdown.base.nm_so; int error = 0; struct inpcb *inp; struct tcpcb *tp; COMMON_START(so, inp, 0); socantsendmore(so); tp = tcp_usrclosed(tp); if (tp) error = tcp_output(tp); COMMON_END(PRU_SHUTDOWN); }
/* * Mark the connection as being incapable of further output. */ static int tcp_usr_shutdown(struct socket *so) { int s = splnet(); int error = 0; struct inpcb *inp = sotoinpcb(so); struct tcpcb *tp; COMMON_START(); socantsendmore(so); tp = tcp_usrclosed(tp); if (tp) error = tcp_output(tp); COMMON_END(PRU_SHUTDOWN); }
struct tcpcb * tcp_disconnect(struct tcpcb * tp) { struct socket * so = tp->t_inpcb->inp_socket; if (tp->t_state < TCPS_ESTABLISHED) tp = tcp_close(tp); else if ((so->so_options & SO_LINGER) && so->so_linger == 0) tp = tcp_drop(tp, 0); else { soisdisconnecting(so); sbflush(&so->so_rcv); tp = tcp_usrclosed(tp); if (tp) (void) tcp_output(tp); } return (tp); }
/* * Initiate (or continue) disconnect. * If embryonic state, just send reset (once). * If in ``let data drain'' option and linger null, just drop. * Otherwise (hard), mark socket disconnecting and drop * current input data; switch states based on user close, and * send segment to peer (with FIN). */ static struct tcpcb * tcp_disconnect(struct tcpcb *tp) { struct socket *so = tp->t_inpcb->inp_socket; if (tp->t_state < TCPS_ESTABLISHED) { tp = tcp_close(tp); } else if ((so->so_options & SO_LINGER) && so->so_linger == 0) { tp = tcp_drop(tp, 0); } else { lwkt_gettoken(&so->so_rcv.ssb_token); soisdisconnecting(so); sbflush(&so->so_rcv.sb); tp = tcp_usrclosed(tp); if (tp) tcp_output(tp); lwkt_reltoken(&so->so_rcv.ssb_token); } return (tp); }
/* * Do a send by putting data in output queue and updating urgent * marker if URG set. Possibly send more data. Unlike the other * pru_*() routines, the mbuf chains are our responsibility. We * must either enqueue them or free them. The other pru_* routines * generally are caller-frees. */ static int tcp_usr_send(struct socket *so, int flags, struct mbuf *m, struct sockaddr *nam, struct mbuf *control, struct thread *td) { int error = 0; struct inpcb *inp; struct tcpcb *tp = NULL; int headlocked = 0; #ifdef INET6 int isipv6; #endif TCPDEBUG0; /* * We require the pcbinfo lock in two cases: * * (1) An implied connect is taking place, which can result in * binding IPs and ports and hence modification of the pcb hash * chains. * * (2) PRUS_EOF is set, resulting in explicit close on the send. */ if ((nam != NULL) || (flags & PRUS_EOF)) { INP_INFO_WLOCK(&V_tcbinfo); headlocked = 1; } inp = sotoinpcb(so); KASSERT(inp != NULL, ("tcp_usr_send: inp == NULL")); INP_WLOCK(inp); if (inp->inp_flags & (INP_TIMEWAIT | INP_DROPPED)) { if (control) m_freem(control); if (m) m_freem(m); error = ECONNRESET; goto out; } #ifdef INET6 isipv6 = nam && nam->sa_family == AF_INET6; #endif /* INET6 */ tp = intotcpcb(inp); TCPDEBUG1(); if (control) { /* TCP doesn't do control messages (rights, creds, etc) */ if (control->m_len) { m_freem(control); if (m) m_freem(m); error = EINVAL; goto out; } m_freem(control); /* empty control, just free it */ } if (!(flags & PRUS_OOB)) { sbappendstream(&so->so_snd, m); if (nam && tp->t_state < TCPS_SYN_SENT) { /* * Do implied connect if not yet connected, * initialize window to default value, and * initialize maxseg/maxopd using peer's cached * MSS. */ INP_INFO_WLOCK_ASSERT(&V_tcbinfo); #ifdef INET6 if (isipv6) error = tcp6_connect(tp, nam, td); else #endif /* INET6 */ error = tcp_connect(tp, nam, td); if (error) goto out; tp->snd_wnd = TTCP_CLIENT_SND_WND; tcp_mss(tp, -1); } if (flags & PRUS_EOF) { /* * Close the send side of the connection after * the data is sent. */ INP_INFO_WLOCK_ASSERT(&V_tcbinfo); socantsendmore(so); tcp_usrclosed(tp); } if (headlocked) { INP_INFO_WUNLOCK(&V_tcbinfo); headlocked = 0; } if (!(inp->inp_flags & INP_DROPPED)) { if (flags & PRUS_MORETOCOME) tp->t_flags |= TF_MORETOCOME; error = tcp_output_send(tp); if (flags & PRUS_MORETOCOME) tp->t_flags &= ~TF_MORETOCOME; } } else { /* * XXXRW: PRUS_EOF not implemented with PRUS_OOB? */ SOCKBUF_LOCK(&so->so_snd); if (sbspace(&so->so_snd) < -512) { SOCKBUF_UNLOCK(&so->so_snd); m_freem(m); error = ENOBUFS; goto out; } /* * According to RFC961 (Assigned Protocols), * the urgent pointer points to the last octet * of urgent data. We continue, however, * to consider it to indicate the first octet * of data past the urgent section. * Otherwise, snd_up should be one lower. */ sbappendstream_locked(&so->so_snd, m); SOCKBUF_UNLOCK(&so->so_snd); if (nam && tp->t_state < TCPS_SYN_SENT) { /* * Do implied connect if not yet connected, * initialize window to default value, and * initialize maxseg/maxopd using peer's cached * MSS. */ INP_INFO_WLOCK_ASSERT(&V_tcbinfo); #ifdef INET6 if (isipv6) error = tcp6_connect(tp, nam, td); else #endif /* INET6 */ error = tcp_connect(tp, nam, td); if (error) goto out; tp->snd_wnd = TTCP_CLIENT_SND_WND; tcp_mss(tp, -1); INP_INFO_WUNLOCK(&V_tcbinfo); headlocked = 0; } else if (nam) { INP_INFO_WUNLOCK(&V_tcbinfo); headlocked = 0; } tp->snd_up = tp->snd_una + so->so_snd.sb_cc; tp->t_flags |= TF_FORCEDATA; error = tcp_output_send(tp); tp->t_flags &= ~TF_FORCEDATA; } out: TCPDEBUG2((flags & PRUS_OOB) ? PRU_SENDOOB : ((flags & PRUS_EOF) ? PRU_SEND_EOF : PRU_SEND)); INP_WUNLOCK(inp); if (headlocked) INP_INFO_WUNLOCK(&V_tcbinfo); return (error); }
/* * Do a send by putting data in output queue and updating urgent * marker if URG set. Possibly send more data. Unlike the other * pru_*() routines, the mbuf chains are our responsibility. We * must either enqueue them or free them. The other pru_* routines * generally are caller-frees. */ static int tcp_usr_send(struct socket *so, int flags, struct mbuf *m, struct sockaddr *nam, struct mbuf *control, struct proc *p) { int s = splnet(); int error = 0; struct inpcb *inp = sotoinpcb(so); struct tcpcb *tp; #ifdef INET6 int isipv6; #endif TCPDEBUG0; if (inp == NULL) { /* * OOPS! we lost a race, the TCP session got reset after * we checked SS_CANTSENDMORE, eg: while doing uiomove or a * network interrupt in the non-splnet() section of sosend(). */ if (m) m_freem(m); if (control) m_freem(control); error = ECONNRESET; /* XXX EPIPE? */ tp = NULL; TCPDEBUG1(); goto out; } #ifdef INET6 isipv6 = nam && nam->sa_family == AF_INET6; #endif /* INET6 */ tp = intotcpcb(inp); TCPDEBUG1(); if (control) { /* TCP doesn't do control messages (rights, creds, etc) */ if (control->m_len) { m_freem(control); if (m) m_freem(m); error = EINVAL; goto out; } m_freem(control); /* empty control, just free it */ } if(!(flags & PRUS_OOB)) { sbappend(&so->so_snd, m); if (nam && tp->t_state < TCPS_SYN_SENT) { /* * Do implied connect if not yet connected, * initialize window to default value, and * initialize maxseg/maxopd using peer's cached * MSS. */ #ifdef INET6 if (isipv6) error = tcp6_connect(tp, nam, p); else #endif /* INET6 */ error = tcp_connect(tp, nam, p); if (error) goto out; tp->snd_wnd = TTCP_CLIENT_SND_WND; tcp_mss(tp, -1); } if (flags & PRUS_EOF) { /* * Close the send side of the connection after * the data is sent. */ socantsendmore(so); tp = tcp_usrclosed(tp); } if (tp != NULL) { if (flags & PRUS_MORETOCOME) tp->t_flags |= TF_MORETOCOME; error = tcp_output(tp); if (flags & PRUS_MORETOCOME) tp->t_flags &= ~TF_MORETOCOME; } } else { if (sbspace(&so->so_snd) < -512) { m_freem(m); error = ENOBUFS; goto out; } /* * According to RFC961 (Assigned Protocols), * the urgent pointer points to the last octet * of urgent data. We continue, however, * to consider it to indicate the first octet * of data past the urgent section. * Otherwise, snd_up should be one lower. */ sbappend(&so->so_snd, m); if (nam && tp->t_state < TCPS_SYN_SENT) { /* * Do implied connect if not yet connected, * initialize window to default value, and * initialize maxseg/maxopd using peer's cached * MSS. */ #ifdef INET6 if (isipv6) error = tcp6_connect(tp, nam, p); else #endif /* INET6 */ error = tcp_connect(tp, nam, p); if (error) goto out; tp->snd_wnd = TTCP_CLIENT_SND_WND; tcp_mss(tp, -1); } tp->snd_up = tp->snd_una + so->so_snd.sb_cc; tp->t_force = 1; error = tcp_output(tp); tp->t_force = 0; } COMMON_END((flags & PRUS_OOB) ? PRU_SENDOOB : ((flags & PRUS_EOF) ? PRU_SEND_EOF : PRU_SEND)); }
static int tcp_connect_oncpu(struct tcpcb *tp, int flags, struct mbuf *m, struct sockaddr_in *sin, struct sockaddr_in *if_sin) { struct inpcb *inp = tp->t_inpcb, *oinp; struct socket *so = inp->inp_socket; struct route *ro = &inp->inp_route; oinp = in_pcblookup_hash(&tcbinfo[mycpu->gd_cpuid], sin->sin_addr, sin->sin_port, (inp->inp_laddr.s_addr != INADDR_ANY ? inp->inp_laddr : if_sin->sin_addr), inp->inp_lport, 0, NULL); if (oinp != NULL) { m_freem(m); return (EADDRINUSE); } if (inp->inp_laddr.s_addr == INADDR_ANY) inp->inp_laddr = if_sin->sin_addr; inp->inp_faddr = sin->sin_addr; inp->inp_fport = sin->sin_port; inp->inp_cpcbinfo = &tcbinfo[mycpu->gd_cpuid]; in_pcbinsconnhash(inp); /* * We are now on the inpcb's owner CPU, if the cached route was * freed because the rtentry's owner CPU is not the current CPU * (e.g. in tcp_connect()), then we try to reallocate it here with * the hope that a rtentry may be cloned from a RTF_PRCLONING * rtentry. */ if (!(inp->inp_socket->so_options & SO_DONTROUTE) && /*XXX*/ ro->ro_rt == NULL) { bzero(&ro->ro_dst, sizeof(struct sockaddr_in)); ro->ro_dst.sa_family = AF_INET; ro->ro_dst.sa_len = sizeof(struct sockaddr_in); ((struct sockaddr_in *)&ro->ro_dst)->sin_addr = sin->sin_addr; rtalloc(ro); } /* * Now that no more errors can occur, change the protocol processing * port to the current thread (which is the correct thread). * * Create TCP timer message now; we are on the tcpcb's owner * CPU/thread. */ tcp_create_timermsg(tp, &curthread->td_msgport); /* * Compute window scaling to request. Use a larger scaling then * needed for the initial receive buffer in case the receive buffer * gets expanded. */ if (tp->request_r_scale < TCP_MIN_WINSHIFT) tp->request_r_scale = TCP_MIN_WINSHIFT; while (tp->request_r_scale < TCP_MAX_WINSHIFT && (TCP_MAXWIN << tp->request_r_scale) < so->so_rcv.ssb_hiwat ) { tp->request_r_scale++; } soisconnecting(so); tcpstat.tcps_connattempt++; tp->t_state = TCPS_SYN_SENT; tcp_callout_reset(tp, tp->tt_keep, tcp_keepinit, tcp_timer_keep); tp->iss = tcp_new_isn(tp); tcp_sendseqinit(tp); if (m) { ssb_appendstream(&so->so_snd, m); m = NULL; if (flags & PRUS_OOB) tp->snd_up = tp->snd_una + so->so_snd.ssb_cc; } /* * Close the send side of the connection after * the data is sent if flagged. */ if ((flags & (PRUS_OOB|PRUS_EOF)) == PRUS_EOF) { socantsendmore(so); tp = tcp_usrclosed(tp); } return (tcp_output(tp)); }
/* * Do a send by putting data in output queue and updating urgent * marker if URG set. Possibly send more data. Unlike the other * pru_*() routines, the mbuf chains are our responsibility. We * must either enqueue them or free them. The other pru_* routines * generally are caller-frees. */ static void tcp_usr_send(netmsg_t msg) { struct socket *so = msg->send.base.nm_so; int flags = msg->send.nm_flags; struct mbuf *m = msg->send.nm_m; struct sockaddr *nam = msg->send.nm_addr; struct mbuf *control = msg->send.nm_control; struct thread *td = msg->send.nm_td; int error = 0; struct inpcb *inp; struct tcpcb *tp; #ifdef INET6 int isipv6; #endif TCPDEBUG0; inp = so->so_pcb; if (inp == NULL) { /* * OOPS! we lost a race, the TCP session got reset after * we checked SS_CANTSENDMORE, eg: while doing uiomove or a * network interrupt in the non-critical section of sosend(). */ m_freem(m); if (control) m_freem(control); error = ECONNRESET; /* XXX EPIPE? */ tp = NULL; TCPDEBUG1(); goto out; } #ifdef INET6 isipv6 = nam && nam->sa_family == AF_INET6; #endif /* INET6 */ tp = intotcpcb(inp); TCPDEBUG1(); if (control) { /* TCP doesn't do control messages (rights, creds, etc) */ if (control->m_len) { m_freem(control); m_freem(m); error = EINVAL; goto out; } m_freem(control); /* empty control, just free it */ } /* * Don't let too much OOB data build up */ if (flags & PRUS_OOB) { if (ssb_space(&so->so_snd) < -512) { m_freem(m); error = ENOBUFS; goto out; } } /* * Do implied connect if not yet connected. Any data sent * with the connect is handled by tcp_connect() and friends. * * NOTE! PROTOCOL THREAD MAY BE CHANGED BY THE CONNECT! */ if (nam && tp->t_state < TCPS_SYN_SENT) { kprintf("implied fallback\n"); msg->connect.nm_nam = nam; msg->connect.nm_td = td; msg->connect.nm_m = m; msg->connect.nm_flags = flags; msg->connect.nm_reconnect = NMSG_RECONNECT_FALLBACK; #ifdef INET6 if (isipv6) tcp6_connect(msg); else #endif /* INET6 */ tcp_connect(msg); /* msg invalid now */ return; } /* * Pump the data into the socket. */ if (m) ssb_appendstream(&so->so_snd, m); if (flags & PRUS_OOB) { /* * According to RFC961 (Assigned Protocols), * the urgent pointer points to the last octet * of urgent data. We continue, however, * to consider it to indicate the first octet * of data past the urgent section. * Otherwise, snd_up should be one lower. */ tp->snd_up = tp->snd_una + so->so_snd.ssb_cc; tp->t_flags |= TF_FORCE; error = tcp_output(tp); tp->t_flags &= ~TF_FORCE; } else { if (flags & PRUS_EOF) { /* * Close the send side of the connection after * the data is sent. */ socantsendmore(so); tp = tcp_usrclosed(tp); } if (tp != NULL) { if (flags & PRUS_MORETOCOME) tp->t_flags |= TF_MORETOCOME; error = tcp_output(tp); if (flags & PRUS_MORETOCOME) tp->t_flags &= ~TF_MORETOCOME; } } COMMON_END((flags & PRUS_OOB) ? PRU_SENDOOB : ((flags & PRUS_EOF) ? PRU_SEND_EOF : PRU_SEND)); }
static int tcp6_connect_oncpu(struct tcpcb *tp, int flags, struct mbuf **mp, struct sockaddr_in6 *sin6, struct in6_addr *addr6) { struct mbuf *m = *mp; struct inpcb *inp = tp->t_inpcb; struct socket *so = inp->inp_socket; struct inpcb *oinp; /* * Cannot simply call in_pcbconnect, because there might be an * earlier incarnation of this same connection still in * TIME_WAIT state, creating an ADDRINUSE error. */ oinp = in6_pcblookup_hash(inp->inp_cpcbinfo, &sin6->sin6_addr, sin6->sin6_port, (IN6_IS_ADDR_UNSPECIFIED(&inp->in6p_laddr) ? addr6 : &inp->in6p_laddr), inp->inp_lport, 0, NULL); if (oinp) return (EADDRINUSE); if (IN6_IS_ADDR_UNSPECIFIED(&inp->in6p_laddr)) inp->in6p_laddr = *addr6; inp->in6p_faddr = sin6->sin6_addr; inp->inp_fport = sin6->sin6_port; if ((sin6->sin6_flowinfo & IPV6_FLOWINFO_MASK) != 0) inp->in6p_flowinfo = sin6->sin6_flowinfo; in_pcbinsconnhash(inp); /* * Now that no more errors can occur, change the protocol processing * port to the current thread (which is the correct thread). * * Create TCP timer message now; we are on the tcpcb's owner * CPU/thread. */ tcp_create_timermsg(tp, &curthread->td_msgport); /* Compute window scaling to request. */ if (tp->request_r_scale < TCP_MIN_WINSHIFT) tp->request_r_scale = TCP_MIN_WINSHIFT; while (tp->request_r_scale < TCP_MAX_WINSHIFT && (TCP_MAXWIN << tp->request_r_scale) < so->so_rcv.ssb_hiwat) { tp->request_r_scale++; } soisconnecting(so); tcpstat.tcps_connattempt++; tp->t_state = TCPS_SYN_SENT; tcp_callout_reset(tp, tp->tt_keep, tcp_keepinit, tcp_timer_keep); tp->iss = tcp_new_isn(tp); tcp_sendseqinit(tp); if (m) { ssb_appendstream(&so->so_snd, m); *mp = NULL; if (flags & PRUS_OOB) tp->snd_up = tp->snd_una + so->so_snd.ssb_cc; } /* * Close the send side of the connection after * the data is sent if flagged. */ if ((flags & (PRUS_OOB|PRUS_EOF)) == PRUS_EOF) { socantsendmore(so); tp = tcp_usrclosed(tp); } return (tcp_output(tp)); }
/* * Do a send by putting data in output queue and updating urgent * marker if URG set. Possibly send more data. Unlike the other * pru_*() routines, the mbuf chains are our responsibility. We * must either enqueue them or free them. The other pru_* routines * generally are caller-frees. */ static void tcp_usr_send(netmsg_t msg) { struct socket *so = msg->send.base.nm_so; int flags = msg->send.nm_flags; struct mbuf *m = msg->send.nm_m; int error = 0; struct inpcb *inp; struct tcpcb *tp; TCPDEBUG0; KKASSERT(msg->send.nm_control == NULL); KKASSERT(msg->send.nm_addr == NULL); KKASSERT((flags & PRUS_FREEADDR) == 0); inp = so->so_pcb; if (inp == NULL) { /* * OOPS! we lost a race, the TCP session got reset after * we checked SS_CANTSENDMORE, eg: while doing uiomove or a * network interrupt in the non-critical section of sosend(). */ m_freem(m); error = ECONNRESET; /* XXX EPIPE? */ tp = NULL; TCPDEBUG1(); goto out; } tp = intotcpcb(inp); TCPDEBUG1(); #ifdef foo /* * This is no longer necessary, since: * - sosendtcp() has already checked it for us * - It does not work with asynchronized send */ /* * Don't let too much OOB data build up */ if (flags & PRUS_OOB) { if (ssb_space(&so->so_snd) < -512) { m_freem(m); error = ENOBUFS; goto out; } } #endif /* * Pump the data into the socket. */ if (m) { ssb_appendstream(&so->so_snd, m); sowwakeup(so); } if (flags & PRUS_OOB) { /* * According to RFC961 (Assigned Protocols), * the urgent pointer points to the last octet * of urgent data. We continue, however, * to consider it to indicate the first octet * of data past the urgent section. * Otherwise, snd_up should be one lower. */ tp->snd_up = tp->snd_una + so->so_snd.ssb_cc; tp->t_flags |= TF_FORCE; error = tcp_output(tp); tp->t_flags &= ~TF_FORCE; } else { if (flags & PRUS_EOF) { /* * Close the send side of the connection after * the data is sent. */ socantsendmore(so); tp = tcp_usrclosed(tp); } if (tp != NULL && !tcp_output_pending(tp)) { if (flags & PRUS_MORETOCOME) tp->t_flags |= TF_MORETOCOME; error = tcp_output_fair(tp); if (flags & PRUS_MORETOCOME) tp->t_flags &= ~TF_MORETOCOME; } } COMMON_END1((flags & PRUS_OOB) ? PRU_SENDOOB : ((flags & PRUS_EOF) ? PRU_SEND_EOF : PRU_SEND), (flags & PRUS_NOREPLY)); }
int tcp_usrreq(struct socket * so, struct mbuf * m, struct mbuf * nam) { struct inpcb * inp; struct tcpcb * tp; int error = 0; int req; #ifdef DO_TCPTRACE int ostate; #endif req = so->so_req; /* get request from socket struct */ inp = sotoinpcb(so); /* * When a TCP is attached to a socket, then there will be * a (struct inpcb) pointed at by the socket, and this * structure will point at a subsidary (struct tcpcb). */ if (inp == 0 && req != PRU_ATTACH) { return (EINVAL); } if (inp) tp = intotcpcb(inp); else /* inp and tp not set, make sure this is OK: */ { if (req == PRU_ATTACH) tp = NULL; /* stifle compiler warnings about using unassigned tp*/ else { dtrap(); /* programming error? */ return EINVAL; } } switch (req) { /* * TCP attaches to socket via PRU_ATTACH, reserving space, * and an internet control block. */ case PRU_ATTACH: if (inp) { error = EISCONN; break; } error = tcp_attach(so); if (error) break; if ((so->so_options & SO_LINGER) && so->so_linger == 0) so->so_linger = TCP_LINGERTIME; #ifdef DO_TCPTRACE SETTP(tp, sototcpcb(so)); #endif break; /* * PRU_DETACH detaches the TCP protocol from the socket. * If the protocol state is non-embryonic, then can't * do this directly: have to initiate a PRU_DISCONNECT, * which may finish later; embryonic TCB's can just * be discarded here. */ case PRU_DETACH: if (tp->t_state > TCPS_LISTEN) SETTP(tp, tcp_disconnect(tp)); else SETTP(tp, tcp_close(tp)); break; /* * Give the socket an address. */ case PRU_BIND: /* bind is quite different for IPv4 and v6, so we use two * seperate pcbbind routines. so_domain was checked for * validity way up in t_bind() */ #ifdef IP_V4 if(inp->inp_socket->so_domain == AF_INET) { error = in_pcbbind(inp, nam); break; } #endif /* IP_V4 */ #ifdef IP_V6 if(inp->inp_socket->so_domain == AF_INET6) { error = ip6_pcbbind(inp, nam); break; } #endif /* IP_V6 */ dtrap(); /* not v4 or v6? */ error = EINVAL; break; /* * Prepare to accept connections. */ case PRU_LISTEN: if (inp->inp_lport == 0) error = in_pcbbind(inp, (struct mbuf *)0); if (error == 0) tp->t_state = TCPS_LISTEN; break; /* * Initiate connection to peer. * Create a template for use in transmissions on this connection. * Enter SYN_SENT state, and mark socket as connecting. * Start keep-alive timer, and seed output sequence space. * Send initial segment on connection. */ case PRU_CONNECT: if (inp->inp_lport == 0) { #ifdef IP_V4 #ifndef IP_V6 /* v4 only */ error = in_pcbbind(inp, (struct mbuf *)0); #else /* dual mode */ if(so->so_domain == AF_INET) error = in_pcbbind(inp, (struct mbuf *)0); else error = ip6_pcbbind(inp, (struct mbuf *)0); #endif /* end dual mode code */ #else /* no v4, v6 only */ error = ip6_pcbbind(inp, (struct mbuf *)0); #endif /* end v6 only */ if (error) break; } #ifdef IP_V4 #ifndef IP_V6 /* v4 only */ error = in_pcbconnect(inp, nam); #else /* dual mode */ if(so->so_domain == AF_INET) error = in_pcbconnect(inp, nam); else error = ip6_pcbconnect(inp, nam); #endif /* end dual mode code */ #else /* no v4, v6 only */ error = ip6_pcbconnect(inp, nam); #endif /* end v6 only */ if (error) break; tp->t_template = tcp_template(tp); if (tp->t_template == 0) { #ifdef IP_V4 #ifndef IP_V6 /* v4 only */ in_pcbdisconnect(inp); #else /* dual mode */ if(so->so_domain == AF_INET) in_pcbdisconnect(inp); else ip6_pcbdisconnect(inp); #endif /* end dual mode code */ #else /* no v4, v6 only */ ip6_pcbdisconnect(inp); #endif /* end v6 only */ error = ENOBUFS; break; } soisconnecting(so); tcpstat.tcps_connattempt++; tp->t_state = TCPS_SYN_SENT; tp->t_timer[TCPT_KEEP] = TCPTV_KEEP_INIT; tp->iss = tcp_iss; tcp_iss += (tcp_seq)(TCP_ISSINCR/2); tcp_sendseqinit(tp); error = tcp_output(tp); if (!error) TCP_MIB_INC(tcpActiveOpens); /* keep MIB stats */ break; /* * Create a TCP connection between two sockets. */ case PRU_CONNECT2: error = EOPNOTSUPP; break; /* * Initiate disconnect from peer. * If connection never passed embryonic stage, just drop; * else if don't need to let data drain, then can just drop anyways, * else have to begin TCP shutdown process: mark socket disconnecting, * drain unread data, state switch to reflect user close, and * send segment (e.g. FIN) to peer. Socket will be really disconnected * when peer sends FIN and acks ours. * * SHOULD IMPLEMENT LATER PRU_CONNECT VIA REALLOC TCPCB. */ case PRU_DISCONNECT: SETTP(tp, tcp_disconnect(tp)); break; /* * Accept a connection. Essentially all the work is * done at higher levels; just return the address * of the peer, storing through addr. */ case PRU_ACCEPT: { struct sockaddr_in * sin = mtod(nam, struct sockaddr_in *); #ifdef IP_V6 struct sockaddr_in6 * sin6 = mtod(nam, struct sockaddr_in6 *); #endif #ifdef IP_V6 if (so->so_domain == AF_INET6) { nam->m_len = sizeof (struct sockaddr_in6); sin6->sin6_port = inp->inp_fport; sin6->sin6_family = AF_INET6; IP6CPY(&sin6->sin6_addr, &inp->ip6_faddr); } #endif #ifdef IP_V4 if (so->so_domain == AF_INET) { nam->m_len = sizeof (struct sockaddr_in); sin->sin_family = AF_INET; sin->sin_port = inp->inp_fport; sin->sin_addr = inp->inp_faddr; } #endif if ( !(so->so_domain == AF_INET) && !(so->so_domain == AF_INET6) ) { dprintf("*** PRU_ACCEPT bad domain = %d\n", so->so_domain); dtrap(); } TCP_MIB_INC(tcpPassiveOpens); /* keep MIB stats */ break; } /* * Mark the connection as being incapable of further output. */ case PRU_SHUTDOWN: socantsendmore(so); tp = tcp_usrclosed(tp); if (tp) error = tcp_output(tp); break; /* * After a receive, possibly send window update to peer. */ case PRU_RCVD: (void) tcp_output(tp); break; /* * Do a send by putting data in output queue and updating urgent * marker if URG set. Possibly send more data. */ case PRU_SEND: if (so->so_pcb == NULL) { /* Return EPIPE error if socket is not connected */ error = EPIPE; break; } sbappend(&so->so_snd, m); error = tcp_output(tp); if (error == ENOBUFS) sbdropend(&so->so_snd,m); /* Remove data from socket buffer */ break; /* * Abort the TCP. */ case PRU_ABORT: SETTP(tp, tcp_drop(tp, ECONNABORTED)); break; case PRU_SENSE: /* ((struct stat *) m)->st_blksize = so->so_snd.sb_hiwat; */ dtrap(); /* does this ever happen? */ return (0); case PRU_RCVOOB: if ((so->so_oobmark == 0 && (so->so_state & SS_RCVATMARK) == 0) || #ifdef SO_OOBINLINE so->so_options & SO_OOBINLINE || #endif tp->t_oobflags & TCPOOB_HADDATA) { error = EINVAL; break; } if ((tp->t_oobflags & TCPOOB_HAVEDATA) == 0) { error = EWOULDBLOCK; break; } m->m_len = 1; *mtod(m, char *) = tp->t_iobc; if ((MBUF2LONG(nam) & MSG_PEEK) == 0) tp->t_oobflags ^= (TCPOOB_HAVEDATA | TCPOOB_HADDATA); break; case PRU_SENDOOB: if (so->so_pcb == NULL) { /* Return EPIPE error if socket is not connected */ error = EPIPE; break; } if (sbspace(&so->so_snd) == 0) { m_freem(m); error = ENOBUFS; break; } /* * According to RFC961 (Assigned Protocols), * the urgent pointer points to the last octet * of urgent data. We continue, however, * to consider it to indicate the first octet * of data past the urgent section. * Otherwise, snd_up should be one lower. */ sbappend(&so->so_snd, m); tp->snd_up = tp->snd_una + so->so_snd.sb_cc; tp->t_force = 1; error = tcp_output(tp); if (error == ENOBUFS) sbdropend(&so->so_snd,m); /* Remove data from socket buffer */ tp->t_force = 0; break; case PRU_SOCKADDR: /* sockaddr and peeraddr have to switch based on IP type */ #ifdef IP_V4 #ifndef IP_V6 /* v4 only */ in_setsockaddr(inp, nam); #else /* dual mode */ if(so->so_domain == AF_INET6) ip6_setsockaddr(inp, nam); else in_setsockaddr(inp, nam); #endif /* dual mode */ #else /* IP_V6 */ ip6_setsockaddr(inp, nam); #endif break; case PRU_PEERADDR: #ifdef IP_V4 #ifndef IP_V6 /* v4 only */ in_setpeeraddr(inp, nam); #else /* dual mode */ if(so->so_domain == AF_INET6) ip6_setpeeraddr(inp, nam); else in_setpeeraddr(inp, nam); #endif /* dual mode */ #else /* IP_V6 */ ip6_setpeeraddr(inp, nam); #endif break; case PRU_SLOWTIMO: SETTP(tp, tcp_timers(tp, (int)MBUF2LONG(nam))); #ifdef DO_TCPTRACE req |= (long)nam << 8; /* for debug's sake */ #endif break; default: panic("tcp_usrreq"); } #ifdef DO_TCPTRACE if (tp && (so->so_options & SO_DEBUG)) tcp_trace("usrreq: state: %d, tcpcb: %x, req: %d", ostate, tp, req); #endif return (error); }