/* * Common subroutine to open a TCP connection to remote host specified * by struct sockaddr_in in mbuf *nam. Call in_pcbbind to assign a local * port number if needed. Call in_pcbconnect_setup to do the routing and * to choose a local host address (interface). If there is an existing * incarnation of the same connection in TIME-WAIT state and if the remote * host was sending CC options and if the connection duration was < MSL, then * truncate the previous TIME-WAIT state and proceed. * Initialize connection parameters and enter SYN-SENT state. */ static int tcp_connect(struct tcpcb *tp, struct sockaddr *nam, struct thread *td) { struct inpcb *inp = tp->t_inpcb, *oinp; struct socket *so = inp->inp_socket; struct in_addr laddr; u_short lport; int error; INP_INFO_WLOCK_ASSERT(&V_tcbinfo); INP_WLOCK_ASSERT(inp); if (inp->inp_lport == 0) { error = in_pcbbind(inp, (struct sockaddr *)0, td->td_ucred); if (error) return error; } /* * 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. */ laddr = inp->inp_laddr; lport = inp->inp_lport; error = in_pcbconnect_setup(inp, nam, &laddr.s_addr, &lport, &inp->inp_faddr.s_addr, &inp->inp_fport, &oinp, td->td_ucred); if (error && oinp == NULL) return error; if (oinp) return EADDRINUSE; inp->inp_laddr = laddr; in_pcbrehash(inp); /* * Compute window scaling to request: * Scale to fit into sweet spot. See tcp_syncache.c. * XXX: This should move to tcp_output(). */ while (tp->request_r_scale < TCP_MAX_WINSHIFT && (TCP_MAXWIN << tp->request_r_scale) < sb_max) tp->request_r_scale++; soisconnecting(so); TCPSTAT_INC(tcps_connattempt); tp->t_state = TCPS_SYN_SENT; tcp_timer_activate(tp, TT_KEEP, tcp_keepinit); tp->iss = tcp_new_isn(tp); tp->t_bw_rtseq = tp->iss; tcp_sendseqinit(tp); return 0; }
static int tcp6_connect(struct tcpcb *tp, struct sockaddr *nam, struct thread *td) { struct inpcb *inp = tp->t_inpcb, *oinp; struct socket *so = inp->inp_socket; struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)nam; struct in6_addr addr6; int error; INP_INFO_WLOCK_ASSERT(&V_tcbinfo); INP_WLOCK_ASSERT(inp); if (inp->inp_lport == 0) { error = in6_pcbbind(inp, (struct sockaddr *)0, td->td_ucred); if (error) return error; } /* * 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. * in6_pcbladdr() also handles scope zone IDs. */ error = in6_pcbladdr(inp, nam, &addr6); if (error) return error; oinp = in6_pcblookup_hash(inp->inp_pcbinfo, &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; /* update flowinfo - draft-itojun-ipv6-flowlabel-api-00 */ inp->inp_flow &= ~IPV6_FLOWLABEL_MASK; if (inp->inp_flags & IN6P_AUTOFLOWLABEL) inp->inp_flow |= (htonl(ip6_randomflowlabel()) & IPV6_FLOWLABEL_MASK); in_pcbrehash(inp); /* Compute window scaling to request. */ while (tp->request_r_scale < TCP_MAX_WINSHIFT && (TCP_MAXWIN << tp->request_r_scale) < sb_max) tp->request_r_scale++; soisconnecting(so); TCPSTAT_INC(tcps_connattempt); tp->t_state = TCPS_SYN_SENT; tcp_timer_activate(tp, TT_KEEP, tcp_keepinit); tp->iss = tcp_new_isn(tp); tp->t_bw_rtseq = tp->iss; tcp_sendseqinit(tp); return 0; }
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)); }
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)); }