#define _IP_VHL
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/sysctl.h>
#include <sys/mbuf.h>
#include <sys/domain.h>
#include <sys/protosw.h>
#include <sys/socket.h>
#include <sys/socketvar.h>
#include <net/route.h>
#include <netinet/in.h>
#include <netinet/in_systm.h>
#include <netinet/ip.h>
#include <netinet/in_pcb.h>
#include <netinet/ip_var.h>
#if INET6
#include <netinet6/in6_pcb.h>
#include <netinet/ip6.h>
#include <netinet6/ip6_var.h>
#endif
#include <netinet/tcp.h>
#define TCPOUTFLAGS
#include <netinet/tcp_fsm.h>
#include <netinet/tcp_seq.h>
#include <netinet/tcp_timer.h>
#include <netinet/tcp_var.h>
#include <netinet/tcpip.h>
#if TCPDEBUG
#include <netinet/tcp_debug.h>
#endif
#include <sys/kdebug.h>
#if IPSEC
#include <netinet6/ipsec.h>
#endif
#define DBG_LAYER_BEG NETDBG_CODE(DBG_NETTCP, 1)
#define DBG_LAYER_END NETDBG_CODE(DBG_NETTCP, 3)
#define DBG_FNC_TCP_OUTPUT NETDBG_CODE(DBG_NETTCP, (4 << 8) | 1)
#ifdef notyet
extern struct mbuf *m_copypack();
#endif
static int path_mtu_discovery = 1;
SYSCTL_INT(_net_inet_tcp, OID_AUTO, path_mtu_discovery, CTLFLAG_RW,
&path_mtu_discovery, 1, "Enable Path MTU Discovery");
int ss_fltsz = 1;
SYSCTL_INT(_net_inet_tcp, OID_AUTO, slowstart_flightsize, CTLFLAG_RW,
&ss_fltsz, 1, "Slow start flight size");
int ss_fltsz_local = 4;
SYSCTL_INT(_net_inet_tcp, OID_AUTO, local_slowstart_flightsize, CTLFLAG_RW,
&ss_fltsz_local, 1, "Slow start flight size for local networks");
int tcp_do_newreno = 0;
SYSCTL_INT(_net_inet_tcp, OID_AUTO, newreno, CTLFLAG_RW, &tcp_do_newreno,
0, "Enable NewReno Algorithms");
int tcp_packet_chaining = 50;
SYSCTL_INT(_net_inet_tcp, OID_AUTO, packetchain, CTLFLAG_RW, &tcp_packet_chaining,
0, "Enable TCP output packet chaining");
struct mbuf *m_copym_with_hdrs(struct mbuf*, int, int, int, struct mbuf**, int*);
static long packchain_newlist = 0;
static long packchain_looped = 0;
static long packchain_sent = 0;
#if IPSEC
extern int ipsec_bypass;
#endif
extern int slowlink_wsize;
extern u_long route_generation;
extern int fw_enable;
extern int ipsec_bypass;
extern vm_size_t so_cache_zone_element_size;
static __inline__ u_int16_t
get_socket_id(struct socket * s)
{
u_int16_t val;
if (so_cache_zone_element_size == 0) {
return (0);
}
val = (u_int16_t)(((u_int32_t)s) / so_cache_zone_element_size);
if (val == 0) {
val = 0xffff;
}
return (val);
}
int
tcp_output(tp)
register struct tcpcb *tp;
{
register struct socket *so = tp->t_inpcb->inp_socket;
register long len, win;
int off, flags, error;
register struct mbuf *m;
struct ip *ip = NULL;
register struct ipovly *ipov = NULL;
#if INET6
struct ip6_hdr *ip6 = NULL;
#endif
register struct tcphdr *th;
u_char opt[TCP_MAXOLEN];
unsigned ipoptlen, optlen, hdrlen;
int idle, sendalot, howmuchsent = 0;
int maxburst = TCP_MAXBURST;
struct rmxp_tao *taop;
struct rmxp_tao tao_noncached;
int last_off = 0;
int m_off;
struct mbuf *m_last = 0;
struct mbuf *m_head = 0;
struct mbuf *packetlist = 0;
struct mbuf *lastpacket = 0;
#if INET6
int isipv6 = tp->t_inpcb->inp_vflag & INP_IPV6 ;
#endif
short packchain_listadd = 0;
u_int16_t socket_id = get_socket_id(so);
idle = (tp->snd_max == tp->snd_una);
if (idle && tp->t_rcvtime >= tp->t_rxtcur) {
if (
#if INET6
(isipv6 && in6_localaddr(&tp->t_inpcb->in6p_faddr)) ||
(!isipv6 &&
#endif
in_localaddr(tp->t_inpcb->inp_faddr)
#if INET6
)
#endif
)
tp->snd_cwnd = tp->t_maxseg * ss_fltsz_local;
else
tp->snd_cwnd = tp->t_maxseg * ss_fltsz;
}
again:
KERNEL_DEBUG(DBG_FNC_TCP_OUTPUT | DBG_FUNC_START, 0,0,0,0,0);
#if INET6
if (isipv6) {
KERNEL_DEBUG(DBG_LAYER_BEG,
((tp->t_inpcb->inp_fport << 16) | tp->t_inpcb->inp_lport),
(((tp->t_inpcb->in6p_laddr.s6_addr16[0] & 0xffff) << 16) |
(tp->t_inpcb->in6p_faddr.s6_addr16[0] & 0xffff)),
sendalot,0,0);
}
else
#endif
{
KERNEL_DEBUG(DBG_LAYER_BEG,
((tp->t_inpcb->inp_fport << 16) | tp->t_inpcb->inp_lport),
(((tp->t_inpcb->inp_laddr.s_addr & 0xffff) << 16) |
(tp->t_inpcb->inp_faddr.s_addr & 0xffff)),
sendalot,0,0);
if ((tp->t_inpcb->inp_route.ro_rt != NULL &&
(tp->t_inpcb->inp_route.ro_rt->generation_id != route_generation)) || (tp->t_inpcb->inp_route.ro_rt == NULL)) {
if (ifa_foraddr(tp->t_inpcb->inp_laddr.s_addr) == 0) {
if (tp->t_state >= TCPS_CLOSE_WAIT) {
tcp_close(tp);
return(EADDRNOTAVAIL);
}
if (!tp->t_timer[TCPT_REXMT]) {
tp->t_timer[TCPT_REXMT] = tp->t_rxtcur;
if (tp->t_timer[TCPT_PERSIST]) {
tp->t_timer[TCPT_PERSIST] = 0;
tp->t_rxtshift = 0;
}
}
if (packetlist) {
error = ip_output_list(packetlist, packchain_listadd, tp->t_inpcb->inp_options, &tp->t_inpcb->inp_route,
(so->so_options & SO_DONTROUTE), 0);
tp->t_lastchain = 0;
}
if (so->so_flags & SOF_NOADDRAVAIL)
return(EADDRNOTAVAIL);
else
return(0);
}
}
}
sendalot = 0;
off = tp->snd_nxt - tp->snd_una;
win = min(tp->snd_wnd, tp->snd_cwnd);
if (tp->t_flags & TF_SLOWLINK && slowlink_wsize > 0)
win = min(win, slowlink_wsize);
flags = tcp_outflags[tp->t_state];
if (tp->t_flags & TF_NEEDFIN)
flags |= TH_FIN;
if (tp->t_flags & TF_NEEDSYN)
flags |= TH_SYN;
if (tp->t_force) {
if (win == 0) {
if (off < so->so_snd.sb_cc)
flags &= ~TH_FIN;
win = 1;
} else {
tp->t_timer[TCPT_PERSIST] = 0;
tp->t_rxtshift = 0;
}
}
len = (long)ulmin(so->so_snd.sb_cc, win) - off;
if ((taop = tcp_gettaocache(tp->t_inpcb)) == NULL) {
taop = &tao_noncached;
bzero(taop, sizeof(*taop));
}
if ((flags & TH_SYN) && SEQ_GT(tp->snd_nxt, tp->snd_una)) {
flags &= ~TH_SYN;
off--, len++;
if (len > 0 && tp->t_state == TCPS_SYN_SENT &&
taop->tao_ccsent == 0) {
if (packetlist) {
error = ip_output_list(packetlist, packchain_listadd, tp->t_inpcb->inp_options, &tp->t_inpcb->inp_route,
(so->so_options & SO_DONTROUTE), 0);
tp->t_lastchain = 0;
}
KERNEL_DEBUG(DBG_FNC_TCP_OUTPUT | DBG_FUNC_END, 0,0,0,0,0);
return 0;
}
}
if ((flags & TH_SYN) &&
((tp->t_flags & TF_NOOPT) || !(tp->t_flags & TF_REQ_CC) ||
((flags & TH_ACK) && !(tp->t_flags & TF_RCVD_CC)))) {
len = 0;
flags &= ~TH_FIN;
}
if (len < 0) {
len = 0;
if (win == 0) {
tp->t_timer[TCPT_REXMT] = 0;
tp->t_rxtshift = 0;
tp->snd_nxt = tp->snd_una;
if (tp->t_timer[TCPT_PERSIST] == 0)
tcp_setpersist(tp);
}
}
if (len > tp->t_maxseg) {
len = tp->t_maxseg;
howmuchsent += len;
sendalot = 1;
}
if (SEQ_LT(tp->snd_nxt + len, tp->snd_una + so->so_snd.sb_cc))
flags &= ~TH_FIN;
if (tp->t_flags & TF_SLOWLINK && slowlink_wsize > 0 )
win = min(sbspace(&so->so_rcv), slowlink_wsize);
else
win = sbspace(&so->so_rcv);
if (len) {
if (len == tp->t_maxseg)
goto send;
if (!(tp->t_flags & TF_MORETOCOME) &&
(idle || tp->t_flags & TF_NODELAY) &&
(tp->t_flags & TF_NOPUSH) == 0 &&
len + off >= so->so_snd.sb_cc)
goto send;
if (tp->t_force)
goto send;
if (len >= tp->max_sndwnd / 2 && tp->max_sndwnd > 0)
goto send;
if (SEQ_LT(tp->snd_nxt, tp->snd_max))
goto send;
}
if (win > 0) {
long adv = min(win, (long)TCP_MAXWIN << tp->rcv_scale) -
(tp->rcv_adv - tp->rcv_nxt);
if (adv >= (long) (2 * tp->t_maxseg))
goto send;
if (2 * adv >= (long) so->so_rcv.sb_hiwat)
goto send;
}
if (tp->t_flags & TF_ACKNOW)
goto send;
if ((flags & TH_RST) ||
((flags & TH_SYN) && (tp->t_flags & TF_NEEDSYN) == 0))
goto send;
if (SEQ_GT(tp->snd_up, tp->snd_una))
goto send;
if (flags & TH_FIN &&
((tp->t_flags & TF_SENTFIN) == 0 || tp->snd_nxt == tp->snd_una))
goto send;
if (so->so_snd.sb_cc && tp->t_timer[TCPT_REXMT] == 0 &&
tp->t_timer[TCPT_PERSIST] == 0) {
tp->t_rxtshift = 0;
tcp_setpersist(tp);
}
if (packetlist) {
error = ip_output_list(packetlist, packchain_listadd, tp->t_inpcb->inp_options, &tp->t_inpcb->inp_route,
(so->so_options & SO_DONTROUTE), 0);
}
KERNEL_DEBUG(DBG_FNC_TCP_OUTPUT | DBG_FUNC_END, 0,0,0,0,0);
return (0);
send:
optlen = 0;
#if INET6
if (isipv6)
hdrlen = sizeof (struct ip6_hdr) + sizeof (struct tcphdr);
else
#endif
hdrlen = sizeof (struct tcpiphdr);
if (flags & TH_SYN) {
tp->snd_nxt = tp->iss;
if ((tp->t_flags & TF_NOOPT) == 0) {
u_short mss;
opt[0] = TCPOPT_MAXSEG;
opt[1] = TCPOLEN_MAXSEG;
mss = htons((u_short) tcp_mssopt(tp));
(void)memcpy(opt + 2, &mss, sizeof(mss));
optlen = TCPOLEN_MAXSEG;
if ((tp->t_flags & TF_REQ_SCALE) &&
((flags & TH_ACK) == 0 ||
(tp->t_flags & TF_RCVD_SCALE))) {
*((u_int32_t *)(opt + optlen)) = htonl(
TCPOPT_NOP << 24 |
TCPOPT_WINDOW << 16 |
TCPOLEN_WINDOW << 8 |
tp->request_r_scale);
optlen += 4;
}
}
}
if ((tp->t_flags & (TF_REQ_TSTMP|TF_NOOPT)) == TF_REQ_TSTMP &&
(flags & TH_RST) == 0 &&
((flags & TH_ACK) == 0 ||
(tp->t_flags & TF_RCVD_TSTMP))) {
u_int32_t *lp = (u_int32_t *)(opt + optlen);
*lp++ = htonl(TCPOPT_TSTAMP_HDR);
*lp++ = htonl(tcp_now);
*lp = htonl(tp->ts_recent);
optlen += TCPOLEN_TSTAMP_APPA;
}
if ((tp->t_flags & (TF_REQ_CC|TF_NOOPT)) == TF_REQ_CC &&
(flags & TH_RST) == 0) {
switch (flags & (TH_SYN|TH_ACK)) {
case TH_ACK:
if (!(tp->t_flags & TF_RCVD_CC))
break;
case 0:
opt[optlen++] = TCPOPT_NOP;
opt[optlen++] = TCPOPT_NOP;
opt[optlen++] = TCPOPT_CC;
opt[optlen++] = TCPOLEN_CC;
*(u_int32_t *)&opt[optlen] = htonl(tp->cc_send);
optlen += 4;
break;
case TH_SYN:
opt[optlen++] = TCPOPT_NOP;
opt[optlen++] = TCPOPT_NOP;
opt[optlen++] = tp->t_flags & TF_SENDCCNEW ?
TCPOPT_CCNEW : TCPOPT_CC;
opt[optlen++] = TCPOLEN_CC;
*(u_int32_t *)&opt[optlen] = htonl(tp->cc_send);
optlen += 4;
break;
case (TH_SYN|TH_ACK):
if (tp->t_flags & TF_RCVD_CC) {
opt[optlen++] = TCPOPT_NOP;
opt[optlen++] = TCPOPT_NOP;
opt[optlen++] = TCPOPT_CC;
opt[optlen++] = TCPOLEN_CC;
*(u_int32_t *)&opt[optlen] =
htonl(tp->cc_send);
optlen += 4;
opt[optlen++] = TCPOPT_NOP;
opt[optlen++] = TCPOPT_NOP;
opt[optlen++] = TCPOPT_CCECHO;
opt[optlen++] = TCPOLEN_CC;
*(u_int32_t *)&opt[optlen] =
htonl(tp->cc_recv);
optlen += 4;
}
break;
}
}
hdrlen += optlen;
#if INET6
if (isipv6)
ipoptlen = ip6_optlen(tp->t_inpcb);
else
#endif
{
if (tp->t_inpcb->inp_options) {
ipoptlen = tp->t_inpcb->inp_options->m_len -
offsetof(struct ipoption, ipopt_list);
} else {
ipoptlen = 0;
}
}
#if IPSEC
if (ipsec_bypass == 0)
ipoptlen += ipsec_hdrsiz_tcp(tp);
#endif
if (len + optlen + ipoptlen > tp->t_maxopd) {
flags &= ~TH_FIN;
len = tp->t_maxopd - optlen - ipoptlen;
howmuchsent += len;
sendalot = 1;
}
#if INET6
if (max_linkhdr + hdrlen > MCLBYTES)
panic("tcphdr too big");
#else
if (max_linkhdr + hdrlen > MHLEN)
panic("tcphdr too big");
#endif
if (len) {
if (tp->t_force && len == 1)
tcpstat.tcps_sndprobe++;
else if (SEQ_LT(tp->snd_nxt, tp->snd_max)) {
tcpstat.tcps_sndrexmitpack++;
tcpstat.tcps_sndrexmitbyte += len;
} else {
tcpstat.tcps_sndpack++;
tcpstat.tcps_sndbyte += len;
}
#ifdef notyet
if ((m = m_copypack(so->so_snd.sb_mb, off,
(int)len, max_linkhdr + hdrlen)) == 0) {
error = ENOBUFS;
goto out;
}
m->m_len += hdrlen;
m->m_data -= hdrlen;
#else
m = NULL;
#if INET6
if (MHLEN < hdrlen + max_linkhdr) {
MGETHDR(m, M_DONTWAIT, MT_HEADER);
if (m == NULL) {
error = ENOBUFS;
goto out;
}
MCLGET(m, M_DONTWAIT);
if ((m->m_flags & M_EXT) == 0) {
m_freem(m);
error = ENOBUFS;
goto out;
}
m->m_data += max_linkhdr;
m->m_len = hdrlen;
}
#endif
if (len <= MHLEN - hdrlen - max_linkhdr) {
if (m == NULL) {
MGETHDR(m, M_DONTWAIT, MT_HEADER);
if (m == NULL) {
error = ENOBUFS;
goto out;
}
m->m_data += max_linkhdr;
m->m_len = hdrlen;
}
if (so->so_snd.sb_mb == NULL || off == -1) {
if (m != NULL) m_freem(m);
error = 0;
goto out;
}
m_copydata(so->so_snd.sb_mb, off, (int) len,
mtod(m, caddr_t) + hdrlen);
m->m_len += len;
} else {
if (m != NULL) {
m->m_next = m_copy(so->so_snd.sb_mb, off, (int) len);
if (m->m_next == 0) {
(void) m_free(m);
error = ENOBUFS;
goto out;
}
} else {
if (m_head != so->so_snd.sb_mb || last_off != off)
m_last = NULL;
last_off = off + len;
m_head = so->so_snd.sb_mb;
if (m_head == NULL) {
error = 0;
goto out;
}
if ((m = m_copym_with_hdrs(so->so_snd.sb_mb, off, (int) len, M_DONTWAIT, &m_last, &m_off)) == NULL) {
error = ENOBUFS;
goto out;
}
m->m_data += max_linkhdr;
m->m_len = hdrlen;
}
}
#endif
if (off + len == so->so_snd.sb_cc)
flags |= TH_PUSH;
} else {
if (tp->t_flags & TF_ACKNOW)
tcpstat.tcps_sndacks++;
else if (flags & (TH_SYN|TH_FIN|TH_RST))
tcpstat.tcps_sndctrl++;
else if (SEQ_GT(tp->snd_up, tp->snd_una))
tcpstat.tcps_sndurg++;
else
tcpstat.tcps_sndwinup++;
MGETHDR(m, M_DONTWAIT, MT_HEADER);
if (m == NULL) {
error = ENOBUFS;
goto out;
}
#if INET6
if (isipv6 && (MHLEN < hdrlen + max_linkhdr) &&
MHLEN >= hdrlen) {
MH_ALIGN(m, hdrlen);
} else
#endif
m->m_data += max_linkhdr;
m->m_len = hdrlen;
}
m->m_pkthdr.rcvif = 0;
#if INET6
if (isipv6) {
ip6 = mtod(m, struct ip6_hdr *);
th = (struct tcphdr *)(ip6 + 1);
tcp_fillheaders(tp, ip6, th);
} else
#endif
{
ip = mtod(m, struct ip *);
ipov = (struct ipovly *)ip;
th = (struct tcphdr *)(ip + 1);
tcp_fillheaders(tp, ip, th);
}
if (flags & TH_FIN && tp->t_flags & TF_SENTFIN &&
tp->snd_nxt == tp->snd_max)
tp->snd_nxt--;
if (len || (flags & (TH_SYN|TH_FIN)) || tp->t_timer[TCPT_PERSIST])
th->th_seq = htonl(tp->snd_nxt);
else
th->th_seq = htonl(tp->snd_max);
th->th_ack = htonl(tp->rcv_nxt);
if (optlen) {
bcopy(opt, th + 1, optlen);
th->th_off = (sizeof (struct tcphdr) + optlen) >> 2;
}
th->th_flags = flags;
if (win < (long)(so->so_rcv.sb_hiwat / 4) && win < (long)tp->t_maxseg)
win = 0;
if (win < (long)(tp->rcv_adv - tp->rcv_nxt))
win = (long)(tp->rcv_adv - tp->rcv_nxt);
if (tp->t_flags & TF_SLOWLINK && slowlink_wsize > 0) {
if (win > (long)slowlink_wsize)
win = slowlink_wsize;
th->th_win = htons((u_short) (win>>tp->rcv_scale));
}
else {
if (win > (long)TCP_MAXWIN << tp->rcv_scale)
win = (long)TCP_MAXWIN << tp->rcv_scale;
th->th_win = htons((u_short) (win>>tp->rcv_scale));
}
if (win == 0)
tp->t_flags |= TF_RXWIN0SENT;
else
tp->t_flags &= ~TF_RXWIN0SENT;
if (SEQ_GT(tp->snd_up, tp->snd_nxt)) {
th->th_urp = htons((u_short)(tp->snd_up - tp->snd_nxt));
th->th_flags |= TH_URG;
} else
tp->snd_up = tp->snd_una;
m->m_pkthdr.len = hdrlen + len;
#if INET6
if (isipv6)
th->th_sum = in6_cksum(m, IPPROTO_TCP, sizeof(struct ip6_hdr),
sizeof(struct tcphdr) + optlen + len);
else
#endif
{
m->m_pkthdr.csum_flags = CSUM_TCP;
m->m_pkthdr.csum_data = offsetof(struct tcphdr, th_sum);
if (len + optlen)
th->th_sum = in_addword(th->th_sum,
htons((u_short)(optlen + len)));
KASSERT(ip->ip_v == IPVERSION,
("%s: IP version incorrect: %d", __FUNCTION__, ip->ip_v));
}
if (tp->t_force == 0 || tp->t_timer[TCPT_PERSIST] == 0) {
tcp_seq startseq = tp->snd_nxt;
if (flags & (TH_SYN|TH_FIN)) {
if (flags & TH_SYN)
tp->snd_nxt++;
if (flags & TH_FIN) {
tp->snd_nxt++;
tp->t_flags |= TF_SENTFIN;
}
}
tp->snd_nxt += len;
if (SEQ_GT(tp->snd_nxt, tp->snd_max)) {
tp->snd_max = tp->snd_nxt;
if (tp->t_rtttime == 0) {
tp->t_rtttime = 1;
tp->t_rtseq = startseq;
tcpstat.tcps_segstimed++;
}
}
if (tp->t_timer[TCPT_REXMT] == 0 &&
tp->snd_nxt != tp->snd_una) {
tp->t_timer[TCPT_REXMT] = tp->t_rxtcur;
if (tp->t_timer[TCPT_PERSIST]) {
tp->t_timer[TCPT_PERSIST] = 0;
tp->t_rxtshift = 0;
}
}
} else
if (SEQ_GT(tp->snd_nxt + len, tp->snd_max))
tp->snd_max = tp->snd_nxt + len;
#if TCPDEBUG
if (so->so_options & SO_DEBUG)
tcp_trace(TA_OUTPUT, tp->t_state, tp, mtod(m, void *), th, 0);
#endif
#if INET6
if (isipv6) {
ip6->ip6_hlim = in6_selecthlim(tp->t_inpcb,
tp->t_inpcb->in6p_route.ro_rt ?
tp->t_inpcb->in6p_route.ro_rt->rt_ifp
: NULL);
#if IPSEC
if (ipsec_bypass == 0 && ipsec_setsocket(m, so) != 0) {
m_freem(m);
error = ENOBUFS;
goto out;
}
#endif
m->m_pkthdr.socket_id = socket_id;
error = ip6_output(m,
tp->t_inpcb->in6p_outputopts,
&tp->t_inpcb->in6p_route,
(so->so_options & SO_DONTROUTE), NULL, NULL, 0);
} else
#endif
{
struct rtentry *rt;
ip->ip_len = m->m_pkthdr.len;
#if INET6
if (isipv6)
ip->ip_ttl = in6_selecthlim(tp->t_inpcb,
tp->t_inpcb->in6p_route.ro_rt ?
tp->t_inpcb->in6p_route.ro_rt->rt_ifp
: NULL);
else
#endif
ip->ip_ttl = tp->t_inpcb->inp_ip_ttl;
ip->ip_tos = tp->t_inpcb->inp_ip_tos;
#if INET6
if (isipv6) {
KERNEL_DEBUG(DBG_LAYER_BEG,
((tp->t_inpcb->inp_fport << 16) | tp->t_inpcb->inp_lport),
(((tp->t_inpcb->in6p_laddr.s6_addr16[0] & 0xffff) << 16) |
(tp->t_inpcb->in6p_faddr.s6_addr16[0] & 0xffff)),
0,0,0);
}
else
#endif
{
KERNEL_DEBUG(DBG_LAYER_BEG,
((tp->t_inpcb->inp_fport << 16) | tp->t_inpcb->inp_lport),
(((tp->t_inpcb->inp_laddr.s_addr & 0xffff) << 16) |
(tp->t_inpcb->inp_faddr.s_addr & 0xffff)),
0,0,0);
}
if (path_mtu_discovery
&& (rt = tp->t_inpcb->inp_route.ro_rt)
&& rt->rt_flags & RTF_UP
&& !(rt->rt_rmx.rmx_locks & RTV_MTU)) {
ip->ip_off |= IP_DF;
}
#if IPSEC
if (ipsec_bypass == 0)
ipsec_setsocket(m, so);
#endif
m->m_pkthdr.socket_id = socket_id;
if (packetlist) {
m->m_nextpkt = NULL;
lastpacket->m_nextpkt = m;
lastpacket = m;
packchain_listadd++;
}
else {
m->m_nextpkt = NULL;
packchain_newlist++;
packetlist = lastpacket = m;
packchain_listadd=0;
}
if ((ipsec_bypass == 0) || fw_enable || sendalot == 0 || (tp->t_state != TCPS_ESTABLISHED) ||
(tp->snd_cwnd <= (tp->snd_wnd / 4)) ||
(tp->t_flags & (TH_PUSH | TF_ACKNOW)) || tp->t_force != 0 ||
packchain_listadd >= tcp_packet_chaining) {
lastpacket->m_nextpkt = 0;
error = ip_output_list(packetlist, packchain_listadd, tp->t_inpcb->inp_options, &tp->t_inpcb->inp_route,
(so->so_options & SO_DONTROUTE), 0);
tp->t_lastchain = packchain_listadd;
packchain_sent++;
packetlist = NULL;
if (error == 0)
howmuchsent = 0;
}
else {
error = 0;
packchain_looped++;
tcpstat.tcps_sndtotal++;
if (win > 0 && SEQ_GT(tp->rcv_nxt+win, tp->rcv_adv))
tp->rcv_adv = tp->rcv_nxt + win;
tp->last_ack_sent = tp->rcv_nxt;
tp->t_flags &= ~(TF_ACKNOW|TF_DELACK);
goto again;
}
}
if (error) {
if (tp->t_force == 0 || !tp->t_timer[TCPT_PERSIST]) {
if ((flags & TH_SYN) == 0)
tp->snd_nxt -= howmuchsent;
}
howmuchsent = 0;
out:
if (error == ENOBUFS) {
if (!tp->t_timer[TCPT_REXMT] &&
!tp->t_timer[TCPT_PERSIST])
tp->t_timer[TCPT_REXMT] = tp->t_rxtcur;
tcp_quench(tp->t_inpcb, 0);
if (packetlist)
m_freem_list(packetlist);
tp->t_lastchain = 0;
KERNEL_DEBUG(DBG_FNC_TCP_OUTPUT | DBG_FUNC_END, 0,0,0,0,0);
return (0);
}
if (error == EMSGSIZE) {
tcp_mtudisc(tp->t_inpcb, 0);
if (packetlist)
m_freem_list(packetlist);
tp->t_lastchain = 0;
KERNEL_DEBUG(DBG_FNC_TCP_OUTPUT | DBG_FUNC_END, 0,0,0,0,0);
return 0;
}
if ((error == EHOSTUNREACH || error == ENETDOWN)
&& TCPS_HAVERCVDSYN(tp->t_state)) {
tp->t_softerror = error;
if (packetlist)
m_freem_list(packetlist);
tp->t_lastchain = 0;
KERNEL_DEBUG(DBG_FNC_TCP_OUTPUT | DBG_FUNC_END, 0,0,0,0,0);
return (0);
}
if (packetlist)
m_freem_list(packetlist);
tp->t_lastchain = 0;
KERNEL_DEBUG(DBG_FNC_TCP_OUTPUT | DBG_FUNC_END, 0,0,0,0,0);
return (error);
}
sentit:
tcpstat.tcps_sndtotal++;
if (win > 0 && SEQ_GT(tp->rcv_nxt+win, tp->rcv_adv))
tp->rcv_adv = tp->rcv_nxt + win;
tp->last_ack_sent = tp->rcv_nxt;
tp->t_flags &= ~(TF_ACKNOW|TF_DELACK);
KERNEL_DEBUG(DBG_FNC_TCP_OUTPUT | DBG_FUNC_END,0,0,0,0,0);
if (sendalot && (!tcp_do_newreno || --maxburst))
goto again;
return (0);
}
void
tcp_setpersist(tp)
register struct tcpcb *tp;
{
int t = ((tp->t_srtt >> 2) + tp->t_rttvar) >> 1;
if (tp->t_timer[TCPT_REXMT])
panic("tcp_setpersist: retransmit pending");
TCPT_RANGESET(tp->t_timer[TCPT_PERSIST],
t * tcp_backoff[tp->t_rxtshift],
TCPTV_PERSMIN, TCPTV_PERSMAX);
if (tp->t_rxtshift < TCP_MAXRXTSHIFT)
tp->t_rxtshift++;
}