+
+/*
+ * Send as many acks as data coalesced. Every other packet when stretch
+ * ACK is not enabled. Every 8 packets, if stretch ACK is enabled.
+ */
+static struct mbuf*
+tcp_send_lroacks(struct tcpcb *tp, struct mbuf *m, struct tcphdr *th)
+{
+ struct mbuf *mnext = NULL, *ack_chain = NULL, *tail = NULL;
+ int count = 0;
+ tcp_seq org_ack = ntohl(th->th_ack);
+ tcp_seq prev_ack = 0;
+ int tack_offset = 28; /* XXX IPv6 not supported */
+ int ack_size = (tp->t_flags & TF_STRETCHACK) ?
+ (maxseg_unacked * tp->t_maxseg) : (tp->t_maxseg << 1);
+ int segs_acked = (tp->t_flags & TF_STRETCHACK) ? maxseg_unacked : 2;
+ struct mbuf *prev_ack_pkt = NULL;
+ struct socket *so = tp->t_inpcb->inp_socket;
+
+ count = tp->t_lropktlen/tp->t_maxseg;
+
+ prev_ack = (org_ack - tp->t_lropktlen) + ack_size;
+ if (prev_ack < org_ack) {
+ ack_chain = m_dup(m, M_DONTWAIT);
+ if (ack_chain) {
+ th->th_ack = htonl(prev_ack);
+ tail = ack_chain;
+ count -= segs_acked; /* accounts for prev_ack packet */
+ count = (count <= segs_acked) ? 0 : count - segs_acked;
+ tcpstat.tcps_sndacks++;
+ so_tc_update_stats(m, so, m_get_service_class(m));
+ } else {
+ return NULL;
+ }
+ }
+ else {
+ tp->t_lropktlen = 0;
+ return NULL;
+ }
+
+ prev_ack_pkt = ack_chain;
+
+ while (count > 0) {
+ if ((prev_ack + ack_size) < org_ack) {
+ prev_ack += ack_size;
+ } else {
+ /*
+ * The last ACK sent must have the ACK number that TCP
+ * thinks is the last sent ACK number.
+ */
+ prev_ack = org_ack;
+ }
+ mnext = m_dup(prev_ack_pkt, M_DONTWAIT);
+ if (mnext) {
+ HTONL(prev_ack);
+ bcopy(&prev_ack, mtod(prev_ack_pkt, caddr_t) + tack_offset, 4);
+ NTOHL(prev_ack);
+ tail->m_nextpkt = mnext;
+ tail = mnext;
+ count -= segs_acked;
+ tcpstat.tcps_sndacks++;
+ so_tc_update_stats(m, so, m_get_service_class(m));
+ if (lrodebug == 5) {
+ printf("%s: lropktlen = %d count = %d, th_ack = %x \n",
+ __func__, tp->t_lropktlen, count,
+ th->th_ack);
+ }
+ } else {
+ if (lrodebug == 5) {
+ printf("%s: failed to alloc mbuf.\n", __func__);
+ }
+ break;
+ }
+ prev_ack_pkt = mnext;
+ }
+ tp->t_lropktlen = 0;
+ return ack_chain;
+}