xref: /freebsd-src/sys/netinet/tcp_lro_hpts.c (revision 8caa2f5351ded559c68ba0cd4713e00136801bd9)
14f9c93f1SGleb Smirnoff /*-
24f9c93f1SGleb Smirnoff  * Copyright (c) 2016-2018 Netflix, Inc.
34f9c93f1SGleb Smirnoff  * Copyright (c) 2016-2021 Mellanox Technologies.
44f9c93f1SGleb Smirnoff  *
54f9c93f1SGleb Smirnoff  * Redistribution and use in source and binary forms, with or without
64f9c93f1SGleb Smirnoff  * modification, are permitted provided that the following conditions
74f9c93f1SGleb Smirnoff  * are met:
84f9c93f1SGleb Smirnoff  * 1. Redistributions of source code must retain the above copyright
94f9c93f1SGleb Smirnoff  *    notice, this list of conditions and the following disclaimer.
104f9c93f1SGleb Smirnoff  * 2. Redistributions in binary form must reproduce the above copyright
114f9c93f1SGleb Smirnoff  *    notice, this list of conditions and the following disclaimer in the
124f9c93f1SGleb Smirnoff  *    documentation and/or other materials provided with the distribution.
134f9c93f1SGleb Smirnoff  *
144f9c93f1SGleb Smirnoff  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
154f9c93f1SGleb Smirnoff  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
164f9c93f1SGleb Smirnoff  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
174f9c93f1SGleb Smirnoff  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
184f9c93f1SGleb Smirnoff  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
194f9c93f1SGleb Smirnoff  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
204f9c93f1SGleb Smirnoff  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
214f9c93f1SGleb Smirnoff  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
224f9c93f1SGleb Smirnoff  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
234f9c93f1SGleb Smirnoff  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
244f9c93f1SGleb Smirnoff  * SUCH DAMAGE.
254f9c93f1SGleb Smirnoff  *
264f9c93f1SGleb Smirnoff  */
274f9c93f1SGleb Smirnoff #include <sys/cdefs.h>
284f9c93f1SGleb Smirnoff #include "opt_inet.h"
294f9c93f1SGleb Smirnoff #include "opt_inet6.h"
304f9c93f1SGleb Smirnoff 
314f9c93f1SGleb Smirnoff #include <sys/param.h>
324f9c93f1SGleb Smirnoff #include <sys/systm.h>
334f9c93f1SGleb Smirnoff #include <sys/kernel.h>
344f9c93f1SGleb Smirnoff #include <sys/malloc.h>
354f9c93f1SGleb Smirnoff #include <sys/mbuf.h>
364f9c93f1SGleb Smirnoff #include <sys/socket.h>
374f9c93f1SGleb Smirnoff #include <sys/socketvar.h>
384f9c93f1SGleb Smirnoff #include <sys/sysctl.h>
394f9c93f1SGleb Smirnoff 
404f9c93f1SGleb Smirnoff #include <net/if.h>
414f9c93f1SGleb Smirnoff #include <net/if_var.h>
424d0c9538SJustin Hibbits #include <net/if_private.h>
434f9c93f1SGleb Smirnoff #include <net/ethernet.h>
444f9c93f1SGleb Smirnoff #include <net/bpf.h>
454f9c93f1SGleb Smirnoff #include <net/vnet.h>
464f9c93f1SGleb Smirnoff #include <net/if_dl.h>
474f9c93f1SGleb Smirnoff #include <net/if_media.h>
484f9c93f1SGleb Smirnoff #include <net/if_types.h>
494f9c93f1SGleb Smirnoff #include <net/infiniband.h>
504f9c93f1SGleb Smirnoff #include <net/if_lagg.h>
51d1ce0121SMichael Tuexen #include <net/pfil.h>
524f9c93f1SGleb Smirnoff 
534f9c93f1SGleb Smirnoff #include <netinet/in.h>
5496ad6401SMichael Tuexen #include <netinet/in_kdtrace.h>
554f9c93f1SGleb Smirnoff #include <netinet/ip6.h>
564f9c93f1SGleb Smirnoff #include <netinet/ip.h>
574f9c93f1SGleb Smirnoff #include <netinet/ip_var.h>
584f9c93f1SGleb Smirnoff #include <netinet/in_pcb.h>
594f9c93f1SGleb Smirnoff #include <netinet6/in6_pcb.h>
60d1ce0121SMichael Tuexen #include <netinet6/ip6_var.h>
614f9c93f1SGleb Smirnoff #include <netinet/tcp.h>
624f9c93f1SGleb Smirnoff #include <netinet/tcp_lro.h>
634f9c93f1SGleb Smirnoff #include <netinet/tcp_var.h>
644f9c93f1SGleb Smirnoff #include <netinet/tcp_hpts.h>
65*8caa2f53SMichael Tuexen #ifdef TCP_BLACKBOX
664f9c93f1SGleb Smirnoff #include <netinet/tcp_log_buf.h>
67*8caa2f53SMichael Tuexen #endif
684f9c93f1SGleb Smirnoff 
694f9c93f1SGleb Smirnoff static void
704f9c93f1SGleb Smirnoff build_ack_entry(struct tcp_ackent *ae, struct tcphdr *th, struct mbuf *m,
714f9c93f1SGleb Smirnoff     uint32_t *ts_ptr, uint16_t iptos)
724f9c93f1SGleb Smirnoff {
734f9c93f1SGleb Smirnoff 	/*
744f9c93f1SGleb Smirnoff 	 * Given a TCP ACK, summarize it down into the small TCP ACK
754f9c93f1SGleb Smirnoff 	 * entry.
764f9c93f1SGleb Smirnoff 	 */
774f9c93f1SGleb Smirnoff 	ae->timestamp = m->m_pkthdr.rcv_tstmp;
784f9c93f1SGleb Smirnoff 	ae->flags = 0;
794f9c93f1SGleb Smirnoff 	if (m->m_flags & M_TSTMP_LRO)
804f9c93f1SGleb Smirnoff 		ae->flags |= TSTMP_LRO;
814f9c93f1SGleb Smirnoff 	else if (m->m_flags & M_TSTMP)
824f9c93f1SGleb Smirnoff 		ae->flags |= TSTMP_HDWR;
83f30c7d56SMichael Tuexen 	ae->seq = th->th_seq;
84f30c7d56SMichael Tuexen 	ae->ack = th->th_ack;
854f9c93f1SGleb Smirnoff 	ae->flags |= tcp_get_flags(th);
864f9c93f1SGleb Smirnoff 	if (ts_ptr != NULL) {
874f9c93f1SGleb Smirnoff 		ae->ts_value = ntohl(ts_ptr[1]);
884f9c93f1SGleb Smirnoff 		ae->ts_echo = ntohl(ts_ptr[2]);
894f9c93f1SGleb Smirnoff 		ae->flags |= HAS_TSTMP;
904f9c93f1SGleb Smirnoff 	}
91f30c7d56SMichael Tuexen 	ae->win = th->th_win;
924f9c93f1SGleb Smirnoff 	ae->codepoint = iptos;
934f9c93f1SGleb Smirnoff }
944f9c93f1SGleb Smirnoff 
954f9c93f1SGleb Smirnoff static inline bool
964f9c93f1SGleb Smirnoff tcp_lro_ack_valid(struct mbuf *m, struct tcphdr *th, uint32_t **ppts, bool *other_opts)
974f9c93f1SGleb Smirnoff {
984f9c93f1SGleb Smirnoff 	/*
994f9c93f1SGleb Smirnoff 	 * This function returns two bits of valuable information.
1004f9c93f1SGleb Smirnoff 	 * a) Is what is present capable of being ack-compressed,
1014f9c93f1SGleb Smirnoff 	 *    we can ack-compress if there is no options or just
1024f9c93f1SGleb Smirnoff 	 *    a timestamp option, and of course the th_flags must
1034f9c93f1SGleb Smirnoff 	 *    be correct as well.
1044f9c93f1SGleb Smirnoff 	 * b) Our other options present such as SACK. This is
1054f9c93f1SGleb Smirnoff 	 *    used to determine if we want to wakeup or not.
1064f9c93f1SGleb Smirnoff 	 */
1074f9c93f1SGleb Smirnoff 	bool ret = true;
1084f9c93f1SGleb Smirnoff 
1094f9c93f1SGleb Smirnoff 	switch (th->th_off << 2) {
1104f9c93f1SGleb Smirnoff 	case (sizeof(*th) + TCPOLEN_TSTAMP_APPA):
1114f9c93f1SGleb Smirnoff 		*ppts = (uint32_t *)(th + 1);
1124f9c93f1SGleb Smirnoff 		/* Check if we have only one timestamp option. */
1134f9c93f1SGleb Smirnoff 		if (**ppts == TCP_LRO_TS_OPTION)
1144f9c93f1SGleb Smirnoff 			*other_opts = false;
1154f9c93f1SGleb Smirnoff 		else {
1164f9c93f1SGleb Smirnoff 			*other_opts = true;
1174f9c93f1SGleb Smirnoff 			ret = false;
1184f9c93f1SGleb Smirnoff 		}
1194f9c93f1SGleb Smirnoff 		break;
1204f9c93f1SGleb Smirnoff 	case (sizeof(*th)):
1214f9c93f1SGleb Smirnoff 		/* No options. */
1224f9c93f1SGleb Smirnoff 		*ppts = NULL;
1234f9c93f1SGleb Smirnoff 		*other_opts = false;
1244f9c93f1SGleb Smirnoff 		break;
1254f9c93f1SGleb Smirnoff 	default:
1264f9c93f1SGleb Smirnoff 		*ppts = NULL;
1274f9c93f1SGleb Smirnoff 		*other_opts = true;
1284f9c93f1SGleb Smirnoff 		ret = false;
1294f9c93f1SGleb Smirnoff 		break;
1304f9c93f1SGleb Smirnoff 	}
1314f9c93f1SGleb Smirnoff 	/* For ACKCMP we only accept ACK, PUSH, ECE and CWR. */
1324f9c93f1SGleb Smirnoff 	if ((tcp_get_flags(th) & ~(TH_ACK | TH_PUSH | TH_ECE | TH_CWR)) != 0)
1334f9c93f1SGleb Smirnoff 		ret = false;
1344f9c93f1SGleb Smirnoff 	/* If it has data on it we cannot compress it */
1354f9c93f1SGleb Smirnoff 	if (m->m_pkthdr.lro_tcp_d_len)
1364f9c93f1SGleb Smirnoff 		ret = false;
1374f9c93f1SGleb Smirnoff 
1384f9c93f1SGleb Smirnoff 	/* ACK flag must be set. */
1394f9c93f1SGleb Smirnoff 	if (!(tcp_get_flags(th) & TH_ACK))
1404f9c93f1SGleb Smirnoff 		ret = false;
1414f9c93f1SGleb Smirnoff 	return (ret);
1424f9c93f1SGleb Smirnoff }
1434f9c93f1SGleb Smirnoff 
1444f9c93f1SGleb Smirnoff static bool
1454f9c93f1SGleb Smirnoff tcp_lro_check_wake_status(struct tcpcb *tp)
1464f9c93f1SGleb Smirnoff {
1474f9c93f1SGleb Smirnoff 
1484f9c93f1SGleb Smirnoff 	if (tp->t_fb->tfb_early_wake_check != NULL)
1494f9c93f1SGleb Smirnoff 		return ((tp->t_fb->tfb_early_wake_check)(tp));
1504f9c93f1SGleb Smirnoff 	return (false);
1514f9c93f1SGleb Smirnoff }
1524f9c93f1SGleb Smirnoff 
153*8caa2f53SMichael Tuexen #ifdef TCP_BLACKBOX
1544f9c93f1SGleb Smirnoff static void
1554f9c93f1SGleb Smirnoff tcp_lro_log(struct tcpcb *tp, const struct lro_ctrl *lc,
1564f9c93f1SGleb Smirnoff     const struct lro_entry *le, const struct mbuf *m,
1574f9c93f1SGleb Smirnoff     int frm, int32_t tcp_data_len, uint32_t th_seq,
1584f9c93f1SGleb Smirnoff     uint32_t th_ack, uint16_t th_win)
1594f9c93f1SGleb Smirnoff {
1604f9c93f1SGleb Smirnoff 	if (tcp_bblogging_on(tp)) {
1614f9c93f1SGleb Smirnoff 		union tcp_log_stackspecific log;
1624f9c93f1SGleb Smirnoff 		struct timeval tv, btv;
1634f9c93f1SGleb Smirnoff 		uint32_t cts;
1644f9c93f1SGleb Smirnoff 
1654f9c93f1SGleb Smirnoff 		cts = tcp_get_usecs(&tv);
1664f9c93f1SGleb Smirnoff 		memset(&log, 0, sizeof(union tcp_log_stackspecific));
1674f9c93f1SGleb Smirnoff 		log.u_bbr.flex8 = frm;
1684f9c93f1SGleb Smirnoff 		log.u_bbr.flex1 = tcp_data_len;
1694f9c93f1SGleb Smirnoff 		if (m)
1704f9c93f1SGleb Smirnoff 			log.u_bbr.flex2 = m->m_pkthdr.len;
1714f9c93f1SGleb Smirnoff 		else
1724f9c93f1SGleb Smirnoff 			log.u_bbr.flex2 = 0;
1734f9c93f1SGleb Smirnoff 		if (le->m_head) {
1744f9c93f1SGleb Smirnoff 			log.u_bbr.flex3 = le->m_head->m_pkthdr.lro_nsegs;
1754f9c93f1SGleb Smirnoff 			log.u_bbr.flex4 = le->m_head->m_pkthdr.lro_tcp_d_len;
1764f9c93f1SGleb Smirnoff 			log.u_bbr.flex5 = le->m_head->m_pkthdr.len;
1774f9c93f1SGleb Smirnoff 			log.u_bbr.delRate = le->m_head->m_flags;
1784f9c93f1SGleb Smirnoff 			log.u_bbr.rttProp = le->m_head->m_pkthdr.rcv_tstmp;
1794f9c93f1SGleb Smirnoff 		}
1804f9c93f1SGleb Smirnoff 		log.u_bbr.inflight = th_seq;
1814f9c93f1SGleb Smirnoff 		log.u_bbr.delivered = th_ack;
1824f9c93f1SGleb Smirnoff 		log.u_bbr.timeStamp = cts;
1834f9c93f1SGleb Smirnoff 		log.u_bbr.epoch = le->next_seq;
1844f9c93f1SGleb Smirnoff 		log.u_bbr.lt_epoch = le->ack_seq;
1854f9c93f1SGleb Smirnoff 		log.u_bbr.pacing_gain = th_win;
1864f9c93f1SGleb Smirnoff 		log.u_bbr.cwnd_gain = le->window;
1874f9c93f1SGleb Smirnoff 		log.u_bbr.lost = curcpu;
1884f9c93f1SGleb Smirnoff 		log.u_bbr.cur_del_rate = (uintptr_t)m;
1894f9c93f1SGleb Smirnoff 		log.u_bbr.bw_inuse = (uintptr_t)le->m_head;
1904f9c93f1SGleb Smirnoff 		bintime2timeval(&lc->lro_last_queue_time, &btv);
1914f9c93f1SGleb Smirnoff 		log.u_bbr.flex6 = tcp_tv_to_usectick(&btv);
1924f9c93f1SGleb Smirnoff 		log.u_bbr.flex7 = le->compressed;
1934f9c93f1SGleb Smirnoff 		log.u_bbr.pacing_gain = le->uncompressed;
1944f9c93f1SGleb Smirnoff 		if (in_epoch(net_epoch_preempt))
1954f9c93f1SGleb Smirnoff 			log.u_bbr.inhpts = 1;
1964f9c93f1SGleb Smirnoff 		else
1974f9c93f1SGleb Smirnoff 			log.u_bbr.inhpts = 0;
1984f9c93f1SGleb Smirnoff 		TCP_LOG_EVENTP(tp, NULL, &tptosocket(tp)->so_rcv,
1994f9c93f1SGleb Smirnoff 		    &tptosocket(tp)->so_snd,
2004f9c93f1SGleb Smirnoff 		    TCP_LOG_LRO, 0, 0, &log, false, &tv);
2014f9c93f1SGleb Smirnoff 	}
2024f9c93f1SGleb Smirnoff }
203*8caa2f53SMichael Tuexen #endif
2044f9c93f1SGleb Smirnoff 
2054f9c93f1SGleb Smirnoff static struct mbuf *
2064f9c93f1SGleb Smirnoff tcp_lro_get_last_if_ackcmp(struct lro_ctrl *lc, struct lro_entry *le,
2074f9c93f1SGleb Smirnoff     struct tcpcb *tp, int32_t *new_m, bool can_append_old_cmp)
2084f9c93f1SGleb Smirnoff {
2094f9c93f1SGleb Smirnoff 	struct mbuf *m;
2104f9c93f1SGleb Smirnoff 
2114f9c93f1SGleb Smirnoff 	/* Look at the last mbuf if any in queue */
2124f9c93f1SGleb Smirnoff 	if (can_append_old_cmp) {
2134f9c93f1SGleb Smirnoff 		m = STAILQ_LAST(&tp->t_inqueue, mbuf, m_stailqpkt);
2144f9c93f1SGleb Smirnoff 		if (m != NULL && (m->m_flags & M_ACKCMP) != 0) {
2154f9c93f1SGleb Smirnoff 			if (M_TRAILINGSPACE(m) >= sizeof(struct tcp_ackent)) {
216*8caa2f53SMichael Tuexen #ifdef TCP_BLACKBOX
2174f9c93f1SGleb Smirnoff 				tcp_lro_log(tp, lc, le, NULL, 23, 0, 0, 0, 0);
218*8caa2f53SMichael Tuexen #endif
2194f9c93f1SGleb Smirnoff 				*new_m = 0;
2204f9c93f1SGleb Smirnoff 				counter_u64_add(tcp_extra_mbuf, 1);
2214f9c93f1SGleb Smirnoff 				return (m);
2224f9c93f1SGleb Smirnoff 			} else {
2234f9c93f1SGleb Smirnoff 				/* Mark we ran out of space */
2244f9c93f1SGleb Smirnoff 				tp->t_flags2 |= TF2_MBUF_L_ACKS;
2254f9c93f1SGleb Smirnoff 			}
2264f9c93f1SGleb Smirnoff 		}
2274f9c93f1SGleb Smirnoff 	}
2284f9c93f1SGleb Smirnoff 	/* Decide mbuf size. */
229*8caa2f53SMichael Tuexen #ifdef TCP_BLACKBOX
2304f9c93f1SGleb Smirnoff 	tcp_lro_log(tp, lc, le, NULL, 21, 0, 0, 0, 0);
231*8caa2f53SMichael Tuexen #endif
2324f9c93f1SGleb Smirnoff 	if (tp->t_flags2 & TF2_MBUF_L_ACKS)
2334f9c93f1SGleb Smirnoff 		m = m_getcl(M_NOWAIT, MT_DATA, M_ACKCMP | M_PKTHDR);
2344f9c93f1SGleb Smirnoff 	else
2354f9c93f1SGleb Smirnoff 		m = m_gethdr(M_NOWAIT, MT_DATA);
2364f9c93f1SGleb Smirnoff 
2374f9c93f1SGleb Smirnoff 	if (__predict_false(m == NULL)) {
2384f9c93f1SGleb Smirnoff 		counter_u64_add(tcp_would_have_but, 1);
2394f9c93f1SGleb Smirnoff 		return (NULL);
2404f9c93f1SGleb Smirnoff 	}
2414f9c93f1SGleb Smirnoff 	counter_u64_add(tcp_comp_total, 1);
2424f9c93f1SGleb Smirnoff 	m->m_pkthdr.rcvif = lc->ifp;
2434f9c93f1SGleb Smirnoff 	m->m_flags |= M_ACKCMP;
2444f9c93f1SGleb Smirnoff 	*new_m = 1;
2454f9c93f1SGleb Smirnoff 	return (m);
2464f9c93f1SGleb Smirnoff }
2474f9c93f1SGleb Smirnoff 
2484f9c93f1SGleb Smirnoff /*
2494f9c93f1SGleb Smirnoff  * Do BPF tap for either ACK_CMP packets or MBUF QUEUE type packets
2504f9c93f1SGleb Smirnoff  * and strip all, but the IPv4/IPv6 header.
2514f9c93f1SGleb Smirnoff  */
2524f9c93f1SGleb Smirnoff static bool
2534f9c93f1SGleb Smirnoff do_bpf_strip_and_compress(struct tcpcb *tp, struct lro_ctrl *lc,
2544f9c93f1SGleb Smirnoff     struct lro_entry *le, struct mbuf **pp, struct mbuf **cmp,
2554f9c93f1SGleb Smirnoff     struct mbuf **mv_to, bool *should_wake, bool bpf_req, bool lagg_bpf_req,
2564f9c93f1SGleb Smirnoff     struct ifnet *lagg_ifp, bool can_append_old_cmp)
2574f9c93f1SGleb Smirnoff {
2584f9c93f1SGleb Smirnoff 	union {
2594f9c93f1SGleb Smirnoff 		void *ptr;
2604f9c93f1SGleb Smirnoff 		struct ip *ip4;
2614f9c93f1SGleb Smirnoff 		struct ip6_hdr *ip6;
2624f9c93f1SGleb Smirnoff 	} l3;
2634f9c93f1SGleb Smirnoff 	struct mbuf *m;
2644f9c93f1SGleb Smirnoff 	struct mbuf *nm;
2654f9c93f1SGleb Smirnoff 	struct tcphdr *th;
2664f9c93f1SGleb Smirnoff 	struct tcp_ackent *ack_ent;
2674f9c93f1SGleb Smirnoff 	uint32_t *ts_ptr;
2684f9c93f1SGleb Smirnoff 	int32_t n_mbuf;
2694f9c93f1SGleb Smirnoff 	bool other_opts, can_compress;
2704f9c93f1SGleb Smirnoff 	uint8_t lro_type;
2714f9c93f1SGleb Smirnoff 	uint16_t iptos;
2724f9c93f1SGleb Smirnoff 	int tcp_hdr_offset;
2734f9c93f1SGleb Smirnoff 	int idx;
2744f9c93f1SGleb Smirnoff 
2754f9c93f1SGleb Smirnoff 	/* Get current mbuf. */
2764f9c93f1SGleb Smirnoff 	m = *pp;
2774f9c93f1SGleb Smirnoff 
2784f9c93f1SGleb Smirnoff 	/* Let the BPF see the packet */
2794f9c93f1SGleb Smirnoff 	if (__predict_false(bpf_req))
2804f9c93f1SGleb Smirnoff 		ETHER_BPF_MTAP(lc->ifp, m);
2814f9c93f1SGleb Smirnoff 
2824f9c93f1SGleb Smirnoff 	if (__predict_false(lagg_bpf_req))
2834f9c93f1SGleb Smirnoff 		ETHER_BPF_MTAP(lagg_ifp, m);
2844f9c93f1SGleb Smirnoff 
2854f9c93f1SGleb Smirnoff 	tcp_hdr_offset = m->m_pkthdr.lro_tcp_h_off;
2864f9c93f1SGleb Smirnoff 	lro_type = le->inner.data.lro_type;
2874f9c93f1SGleb Smirnoff 	switch (lro_type) {
2884f9c93f1SGleb Smirnoff 	case LRO_TYPE_NONE:
2894f9c93f1SGleb Smirnoff 		lro_type = le->outer.data.lro_type;
2904f9c93f1SGleb Smirnoff 		switch (lro_type) {
2914f9c93f1SGleb Smirnoff 		case LRO_TYPE_IPV4_TCP:
2924f9c93f1SGleb Smirnoff 			tcp_hdr_offset -= sizeof(*le->outer.ip4);
2934f9c93f1SGleb Smirnoff 			m->m_pkthdr.lro_etype = ETHERTYPE_IP;
29496ad6401SMichael Tuexen 			IP_PROBE(receive, NULL, NULL, le->outer.ip4, lc->ifp,
29596ad6401SMichael Tuexen 			    le->outer.ip4, NULL);
2964f9c93f1SGleb Smirnoff 			break;
2974f9c93f1SGleb Smirnoff 		case LRO_TYPE_IPV6_TCP:
2984f9c93f1SGleb Smirnoff 			tcp_hdr_offset -= sizeof(*le->outer.ip6);
2994f9c93f1SGleb Smirnoff 			m->m_pkthdr.lro_etype = ETHERTYPE_IPV6;
30096ad6401SMichael Tuexen 			IP_PROBE(receive, NULL, NULL, le->outer.ip6, lc->ifp,
30196ad6401SMichael Tuexen 			    NULL, le->outer.ip6);
3024f9c93f1SGleb Smirnoff 			break;
3034f9c93f1SGleb Smirnoff 		default:
3044f9c93f1SGleb Smirnoff 			goto compressed;
3054f9c93f1SGleb Smirnoff 		}
3064f9c93f1SGleb Smirnoff 		break;
3074f9c93f1SGleb Smirnoff 	case LRO_TYPE_IPV4_TCP:
30896ad6401SMichael Tuexen 		switch (le->outer.data.lro_type) {
30996ad6401SMichael Tuexen 		case LRO_TYPE_IPV4_UDP:
31096ad6401SMichael Tuexen 			IP_PROBE(receive, NULL, NULL, le->outer.ip4, lc->ifp,
31196ad6401SMichael Tuexen 			    le->outer.ip4, NULL);
31296ad6401SMichael Tuexen 			UDP_PROBE(receive, NULL, NULL, le->outer.ip4, NULL,
31396ad6401SMichael Tuexen 			    le->outer.udp);
31496ad6401SMichael Tuexen 			break;
31596ad6401SMichael Tuexen 		case LRO_TYPE_IPV6_UDP:
31696ad6401SMichael Tuexen 			IP_PROBE(receive, NULL, NULL, le->outer.ip6, lc->ifp,
31796ad6401SMichael Tuexen 			    NULL, le->outer.ip6);
31896ad6401SMichael Tuexen 			UDP_PROBE(receive, NULL, NULL, le->outer.ip6, NULL,
31996ad6401SMichael Tuexen 			    le->outer.udp);
32096ad6401SMichael Tuexen 			break;
32196ad6401SMichael Tuexen 		default:
32296ad6401SMichael Tuexen 			__assert_unreachable();
32396ad6401SMichael Tuexen 			break;
32496ad6401SMichael Tuexen 		}
3254f9c93f1SGleb Smirnoff 		tcp_hdr_offset -= sizeof(*le->outer.ip4);
3264f9c93f1SGleb Smirnoff 		m->m_pkthdr.lro_etype = ETHERTYPE_IP;
32796ad6401SMichael Tuexen 		IP_PROBE(receive, NULL, NULL, le->inner.ip4, NULL,
32896ad6401SMichael Tuexen 		    le->inner.ip4, NULL);
3294f9c93f1SGleb Smirnoff 		break;
3304f9c93f1SGleb Smirnoff 	case LRO_TYPE_IPV6_TCP:
33196ad6401SMichael Tuexen 		switch (le->outer.data.lro_type) {
33296ad6401SMichael Tuexen 		case LRO_TYPE_IPV4_UDP:
33396ad6401SMichael Tuexen 			IP_PROBE(receive, NULL, NULL, le->outer.ip4, lc->ifp,
33496ad6401SMichael Tuexen 			    le->outer.ip4, NULL);
33596ad6401SMichael Tuexen 			UDP_PROBE(receive, NULL, NULL, le->outer.ip4, NULL,
33696ad6401SMichael Tuexen 			    le->outer.udp);
33796ad6401SMichael Tuexen 			break;
33896ad6401SMichael Tuexen 		case LRO_TYPE_IPV6_UDP:
33996ad6401SMichael Tuexen 			IP_PROBE(receive, NULL, NULL, le->outer.ip6, lc->ifp,
34096ad6401SMichael Tuexen 			    NULL, le->outer.ip6);
34196ad6401SMichael Tuexen 			UDP_PROBE(receive, NULL, NULL, le->outer.ip6, NULL,
34296ad6401SMichael Tuexen 			    le->outer.udp);
34396ad6401SMichael Tuexen 			break;
34496ad6401SMichael Tuexen 		default:
34596ad6401SMichael Tuexen 			__assert_unreachable();
34696ad6401SMichael Tuexen 			break;
34796ad6401SMichael Tuexen 		}
3484f9c93f1SGleb Smirnoff 		tcp_hdr_offset -= sizeof(*le->outer.ip6);
3494f9c93f1SGleb Smirnoff 		m->m_pkthdr.lro_etype = ETHERTYPE_IPV6;
35096ad6401SMichael Tuexen 		IP_PROBE(receive, NULL, NULL, le->inner.ip6, NULL, NULL,
35196ad6401SMichael Tuexen 		    le->inner.ip6);
3524f9c93f1SGleb Smirnoff 		break;
3534f9c93f1SGleb Smirnoff 	default:
3544f9c93f1SGleb Smirnoff 		goto compressed;
3554f9c93f1SGleb Smirnoff 	}
3564f9c93f1SGleb Smirnoff 
3574f9c93f1SGleb Smirnoff 	MPASS(tcp_hdr_offset >= 0);
3584f9c93f1SGleb Smirnoff 
3594f9c93f1SGleb Smirnoff 	m_adj(m, tcp_hdr_offset);
3604f9c93f1SGleb Smirnoff 	m->m_flags |= M_LRO_EHDRSTRP;
3614f9c93f1SGleb Smirnoff 	m->m_flags &= ~M_ACKCMP;
3624f9c93f1SGleb Smirnoff 	m->m_pkthdr.lro_tcp_h_off -= tcp_hdr_offset;
3634f9c93f1SGleb Smirnoff 
3644f9c93f1SGleb Smirnoff 	th = tcp_lro_get_th(m);
3654f9c93f1SGleb Smirnoff 
3664f9c93f1SGleb Smirnoff 	th->th_sum = 0;		/* TCP checksum is valid. */
367f30c7d56SMichael Tuexen 	tcp_fields_to_host(th);
36896ad6401SMichael Tuexen 	TCP_PROBE5(receive, NULL, tp, m, tp, th);
3694f9c93f1SGleb Smirnoff 
3704f9c93f1SGleb Smirnoff 	/* Check if ACK can be compressed */
3714f9c93f1SGleb Smirnoff 	can_compress = tcp_lro_ack_valid(m, th, &ts_ptr, &other_opts);
3724f9c93f1SGleb Smirnoff 
3734f9c93f1SGleb Smirnoff 	/* Now lets look at the should wake states */
3744f9c93f1SGleb Smirnoff 	if ((other_opts == true) &&
3754f9c93f1SGleb Smirnoff 	    ((tp->t_flags2 & TF2_DONT_SACK_QUEUE) == 0)) {
3764f9c93f1SGleb Smirnoff 		/*
3774f9c93f1SGleb Smirnoff 		 * If there are other options (SACK?) and the
3784f9c93f1SGleb Smirnoff 		 * tcp endpoint has not expressly told us it does
3794f9c93f1SGleb Smirnoff 		 * not care about SACKS, then we should wake up.
3804f9c93f1SGleb Smirnoff 		 */
3814f9c93f1SGleb Smirnoff 		*should_wake = true;
3824f9c93f1SGleb Smirnoff 	} else if (*should_wake == false) {
3834f9c93f1SGleb Smirnoff 		/* Wakeup override check if we are false here  */
3844f9c93f1SGleb Smirnoff 		*should_wake = tcp_lro_check_wake_status(tp);
3854f9c93f1SGleb Smirnoff 	}
3864f9c93f1SGleb Smirnoff 	/* Is the ack compressable? */
3874f9c93f1SGleb Smirnoff 	if (can_compress == false)
3884f9c93f1SGleb Smirnoff 		goto done;
3894f9c93f1SGleb Smirnoff 	/* Does the TCP endpoint support ACK compression? */
3904f9c93f1SGleb Smirnoff 	if ((tp->t_flags2 & TF2_MBUF_ACKCMP) == 0)
3914f9c93f1SGleb Smirnoff 		goto done;
3924f9c93f1SGleb Smirnoff 
3934f9c93f1SGleb Smirnoff 	/* Lets get the TOS/traffic class field */
3944f9c93f1SGleb Smirnoff 	l3.ptr = mtod(m, void *);
3954f9c93f1SGleb Smirnoff 	switch (lro_type) {
3964f9c93f1SGleb Smirnoff 	case LRO_TYPE_IPV4_TCP:
3974f9c93f1SGleb Smirnoff 		iptos = l3.ip4->ip_tos;
3984f9c93f1SGleb Smirnoff 		break;
3994f9c93f1SGleb Smirnoff 	case LRO_TYPE_IPV6_TCP:
4004f9c93f1SGleb Smirnoff 		iptos = IPV6_TRAFFIC_CLASS(l3.ip6);
4014f9c93f1SGleb Smirnoff 		break;
4024f9c93f1SGleb Smirnoff 	default:
4034f9c93f1SGleb Smirnoff 		iptos = 0;	/* Keep compiler happy. */
4044f9c93f1SGleb Smirnoff 		break;
4054f9c93f1SGleb Smirnoff 	}
4064f9c93f1SGleb Smirnoff 	/* Now lets get space if we don't have some already */
4074f9c93f1SGleb Smirnoff 	if (*cmp == NULL) {
4084f9c93f1SGleb Smirnoff new_one:
4094f9c93f1SGleb Smirnoff 		nm = tcp_lro_get_last_if_ackcmp(lc, le, tp, &n_mbuf,
4104f9c93f1SGleb Smirnoff 		    can_append_old_cmp);
4114f9c93f1SGleb Smirnoff 		if (__predict_false(nm == NULL))
4124f9c93f1SGleb Smirnoff 			goto done;
4134f9c93f1SGleb Smirnoff 		*cmp = nm;
4144f9c93f1SGleb Smirnoff 		if (n_mbuf) {
4154f9c93f1SGleb Smirnoff 			/*
4164f9c93f1SGleb Smirnoff 			 *  Link in the new cmp ack to our in-order place,
4174f9c93f1SGleb Smirnoff 			 * first set our cmp ack's next to where we are.
4184f9c93f1SGleb Smirnoff 			 */
4194f9c93f1SGleb Smirnoff 			nm->m_nextpkt = m;
4204f9c93f1SGleb Smirnoff 			(*pp) = nm;
4214f9c93f1SGleb Smirnoff 			/*
4224f9c93f1SGleb Smirnoff 			 * Set it up so mv_to is advanced to our
4234f9c93f1SGleb Smirnoff 			 * compressed ack. This way the caller can
4244f9c93f1SGleb Smirnoff 			 * advance pp to the right place.
4254f9c93f1SGleb Smirnoff 			 */
4264f9c93f1SGleb Smirnoff 			*mv_to = nm;
4274f9c93f1SGleb Smirnoff 			/*
4284f9c93f1SGleb Smirnoff 			 * Advance it here locally as well.
4294f9c93f1SGleb Smirnoff 			 */
4304f9c93f1SGleb Smirnoff 			pp = &nm->m_nextpkt;
4314f9c93f1SGleb Smirnoff 		}
4324f9c93f1SGleb Smirnoff 	} else {
4334f9c93f1SGleb Smirnoff 		/* We have one already we are working on */
4344f9c93f1SGleb Smirnoff 		nm = *cmp;
4354f9c93f1SGleb Smirnoff 		if (M_TRAILINGSPACE(nm) < sizeof(struct tcp_ackent)) {
4364f9c93f1SGleb Smirnoff 			/* We ran out of space */
4374f9c93f1SGleb Smirnoff 			tp->t_flags2 |= TF2_MBUF_L_ACKS;
4384f9c93f1SGleb Smirnoff 			goto new_one;
4394f9c93f1SGleb Smirnoff 		}
4404f9c93f1SGleb Smirnoff 	}
4414f9c93f1SGleb Smirnoff 	MPASS(M_TRAILINGSPACE(nm) >= sizeof(struct tcp_ackent));
4424f9c93f1SGleb Smirnoff 	counter_u64_add(tcp_inp_lro_compressed, 1);
4434f9c93f1SGleb Smirnoff 	le->compressed++;
4444f9c93f1SGleb Smirnoff 	/* We can add in to the one on the tail */
4454f9c93f1SGleb Smirnoff 	ack_ent = mtod(nm, struct tcp_ackent *);
4464f9c93f1SGleb Smirnoff 	idx = (nm->m_len / sizeof(struct tcp_ackent));
4474f9c93f1SGleb Smirnoff 	build_ack_entry(&ack_ent[idx], th, m, ts_ptr, iptos);
4484f9c93f1SGleb Smirnoff 
4494f9c93f1SGleb Smirnoff 	/* Bump the size of both pkt-hdr and len */
4504f9c93f1SGleb Smirnoff 	nm->m_len += sizeof(struct tcp_ackent);
4514f9c93f1SGleb Smirnoff 	nm->m_pkthdr.len += sizeof(struct tcp_ackent);
4524f9c93f1SGleb Smirnoff compressed:
4534f9c93f1SGleb Smirnoff 	/* Advance to next mbuf before freeing. */
4544f9c93f1SGleb Smirnoff 	*pp = m->m_nextpkt;
4554f9c93f1SGleb Smirnoff 	m->m_nextpkt = NULL;
4564f9c93f1SGleb Smirnoff 	m_freem(m);
4574f9c93f1SGleb Smirnoff 	return (true);
4584f9c93f1SGleb Smirnoff done:
4594f9c93f1SGleb Smirnoff 	counter_u64_add(tcp_uncomp_total, 1);
4604f9c93f1SGleb Smirnoff 	le->uncompressed++;
4614f9c93f1SGleb Smirnoff 	return (false);
4624f9c93f1SGleb Smirnoff }
4634f9c93f1SGleb Smirnoff 
4644f9c93f1SGleb Smirnoff static void
4654f9c93f1SGleb Smirnoff tcp_queue_pkts(struct tcpcb *tp, struct lro_entry *le)
4664f9c93f1SGleb Smirnoff {
4674f9c93f1SGleb Smirnoff 
4684f9c93f1SGleb Smirnoff 	INP_WLOCK_ASSERT(tptoinpcb(tp));
4694f9c93f1SGleb Smirnoff 
4704f9c93f1SGleb Smirnoff 	STAILQ_HEAD(, mbuf) q = { le->m_head,
4714f9c93f1SGleb Smirnoff 	    &STAILQ_NEXT(le->m_last_mbuf, m_stailqpkt) };
4724f9c93f1SGleb Smirnoff 	STAILQ_CONCAT(&tp->t_inqueue, &q);
4734f9c93f1SGleb Smirnoff 	le->m_head = NULL;
4744f9c93f1SGleb Smirnoff 	le->m_last_mbuf = NULL;
4754f9c93f1SGleb Smirnoff }
4764f9c93f1SGleb Smirnoff 
4774f9c93f1SGleb Smirnoff static struct tcpcb *
4784f9c93f1SGleb Smirnoff tcp_lro_lookup(struct ifnet *ifp, struct lro_parser *pa)
4794f9c93f1SGleb Smirnoff {
4804f9c93f1SGleb Smirnoff 	struct inpcb *inp;
4814f9c93f1SGleb Smirnoff 
482d1ce0121SMichael Tuexen 	CURVNET_ASSERT_SET();
4834f9c93f1SGleb Smirnoff 	switch (pa->data.lro_type) {
4844f9c93f1SGleb Smirnoff #ifdef INET6
4854f9c93f1SGleb Smirnoff 	case LRO_TYPE_IPV6_TCP:
4864f9c93f1SGleb Smirnoff 		inp = in6_pcblookup(&V_tcbinfo,
4874f9c93f1SGleb Smirnoff 		    &pa->data.s_addr.v6,
4884f9c93f1SGleb Smirnoff 		    pa->data.s_port,
4894f9c93f1SGleb Smirnoff 		    &pa->data.d_addr.v6,
4904f9c93f1SGleb Smirnoff 		    pa->data.d_port,
4914f9c93f1SGleb Smirnoff 		    INPLOOKUP_WLOCKPCB,
4924f9c93f1SGleb Smirnoff 		    ifp);
4934f9c93f1SGleb Smirnoff 		break;
4944f9c93f1SGleb Smirnoff #endif
4954f9c93f1SGleb Smirnoff #ifdef INET
4964f9c93f1SGleb Smirnoff 	case LRO_TYPE_IPV4_TCP:
4974f9c93f1SGleb Smirnoff 		inp = in_pcblookup(&V_tcbinfo,
4984f9c93f1SGleb Smirnoff 		    pa->data.s_addr.v4,
4994f9c93f1SGleb Smirnoff 		    pa->data.s_port,
5004f9c93f1SGleb Smirnoff 		    pa->data.d_addr.v4,
5014f9c93f1SGleb Smirnoff 		    pa->data.d_port,
5024f9c93f1SGleb Smirnoff 		    INPLOOKUP_WLOCKPCB,
5034f9c93f1SGleb Smirnoff 		    ifp);
5044f9c93f1SGleb Smirnoff 		break;
5054f9c93f1SGleb Smirnoff #endif
5064f9c93f1SGleb Smirnoff 	default:
5074f9c93f1SGleb Smirnoff 		return (NULL);
5084f9c93f1SGleb Smirnoff 	}
5094f9c93f1SGleb Smirnoff 
5104f9c93f1SGleb Smirnoff 	return (intotcpcb(inp));
5114f9c93f1SGleb Smirnoff }
5124f9c93f1SGleb Smirnoff 
5132c6fc36aSGleb Smirnoff static int
5142c6fc36aSGleb Smirnoff _tcp_lro_flush_tcphpts(struct lro_ctrl *lc, struct lro_entry *le)
5154f9c93f1SGleb Smirnoff {
5164f9c93f1SGleb Smirnoff 	struct tcpcb *tp;
5174f9c93f1SGleb Smirnoff 	struct mbuf **pp, *cmp, *mv_to;
5184f9c93f1SGleb Smirnoff 	struct ifnet *lagg_ifp;
5194f9c93f1SGleb Smirnoff 	bool bpf_req, lagg_bpf_req, should_wake, can_append_old_cmp;
5204f9c93f1SGleb Smirnoff 
5214f9c93f1SGleb Smirnoff 	/* Check if packet doesn't belongs to our network interface. */
5224f9c93f1SGleb Smirnoff 	if ((tcplro_stacks_wanting_mbufq == 0) ||
5234f9c93f1SGleb Smirnoff 	    (le->outer.data.vlan_id != 0) ||
5244f9c93f1SGleb Smirnoff 	    (le->inner.data.lro_type != LRO_TYPE_NONE))
5254f9c93f1SGleb Smirnoff 		return (TCP_LRO_CANNOT);
5264f9c93f1SGleb Smirnoff 
5274f9c93f1SGleb Smirnoff #ifdef INET6
5284f9c93f1SGleb Smirnoff 	/*
5294f9c93f1SGleb Smirnoff 	 * Be proactive about unspecified IPv6 address in source. As
5304f9c93f1SGleb Smirnoff 	 * we use all-zero to indicate unbounded/unconnected pcb,
5314f9c93f1SGleb Smirnoff 	 * unspecified IPv6 address can be used to confuse us.
5324f9c93f1SGleb Smirnoff 	 *
5334f9c93f1SGleb Smirnoff 	 * Note that packets with unspecified IPv6 destination is
5344f9c93f1SGleb Smirnoff 	 * already dropped in ip6_input.
5354f9c93f1SGleb Smirnoff 	 */
5364f9c93f1SGleb Smirnoff 	if (__predict_false(le->outer.data.lro_type == LRO_TYPE_IPV6_TCP &&
5374f9c93f1SGleb Smirnoff 	    IN6_IS_ADDR_UNSPECIFIED(&le->outer.data.s_addr.v6)))
5384f9c93f1SGleb Smirnoff 		return (TCP_LRO_CANNOT);
5394f9c93f1SGleb Smirnoff 
5404f9c93f1SGleb Smirnoff 	if (__predict_false(le->inner.data.lro_type == LRO_TYPE_IPV6_TCP &&
5414f9c93f1SGleb Smirnoff 	    IN6_IS_ADDR_UNSPECIFIED(&le->inner.data.s_addr.v6)))
5424f9c93f1SGleb Smirnoff 		return (TCP_LRO_CANNOT);
5434f9c93f1SGleb Smirnoff #endif
544d1ce0121SMichael Tuexen 
545d1ce0121SMichael Tuexen 	CURVNET_SET(lc->ifp->if_vnet);
546d1ce0121SMichael Tuexen 	/*
547d1ce0121SMichael Tuexen 	 * Ensure that there are no packet filter hooks which would normally
548d1ce0121SMichael Tuexen 	 * being triggered in ether_demux(), ip_input(), or ip6_input().
549d1ce0121SMichael Tuexen 	 */
550d1ce0121SMichael Tuexen 	if (
551d1ce0121SMichael Tuexen #ifdef INET
552d1ce0121SMichael Tuexen 	    PFIL_HOOKED_IN(V_inet_pfil_head) ||
553d1ce0121SMichael Tuexen #endif
554d1ce0121SMichael Tuexen #ifdef INET6
555d1ce0121SMichael Tuexen 	    PFIL_HOOKED_IN(V_inet6_pfil_head) ||
556d1ce0121SMichael Tuexen #endif
557d1ce0121SMichael Tuexen 	    PFIL_HOOKED_IN(V_link_pfil_head)) {
558d1ce0121SMichael Tuexen 		CURVNET_RESTORE();
559d1ce0121SMichael Tuexen 		return (TCP_LRO_CANNOT);
560d1ce0121SMichael Tuexen 	}
561d1ce0121SMichael Tuexen 
5624f9c93f1SGleb Smirnoff 	/* Lookup inp, if any.  Returns locked TCP inpcb. */
5634f9c93f1SGleb Smirnoff 	tp = tcp_lro_lookup(lc->ifp,
5644f9c93f1SGleb Smirnoff 	    (le->inner.data.lro_type == LRO_TYPE_NONE) ? &le->outer : &le->inner);
565d1ce0121SMichael Tuexen 	CURVNET_RESTORE();
5664f9c93f1SGleb Smirnoff 	if (tp == NULL)
5674f9c93f1SGleb Smirnoff 		return (TCP_LRO_CANNOT);
5684f9c93f1SGleb Smirnoff 
5694f9c93f1SGleb Smirnoff 	counter_u64_add(tcp_inp_lro_locks_taken, 1);
5704f9c93f1SGleb Smirnoff 
5714f9c93f1SGleb Smirnoff 	/* Check if the inp is dead, Jim. */
5724f9c93f1SGleb Smirnoff 	if (tp->t_state == TCPS_TIME_WAIT) {
5734f9c93f1SGleb Smirnoff 		INP_WUNLOCK(tptoinpcb(tp));
5744f9c93f1SGleb Smirnoff 		return (TCP_LRO_CANNOT);
5754f9c93f1SGleb Smirnoff 	}
5764f9c93f1SGleb Smirnoff 	if (tp->t_lro_cpu == HPTS_CPU_NONE && lc->lro_cpu_is_set == 1)
5774f9c93f1SGleb Smirnoff 		tp->t_lro_cpu = lc->lro_last_cpu;
5784f9c93f1SGleb Smirnoff 	/* Check if the transport doesn't support the needed optimizations. */
5794f9c93f1SGleb Smirnoff 	if ((tp->t_flags2 & (TF2_SUPPORTS_MBUFQ | TF2_MBUF_ACKCMP)) == 0) {
5804f9c93f1SGleb Smirnoff 		INP_WUNLOCK(tptoinpcb(tp));
5814f9c93f1SGleb Smirnoff 		return (TCP_LRO_CANNOT);
5824f9c93f1SGleb Smirnoff 	}
5834f9c93f1SGleb Smirnoff 
5844f9c93f1SGleb Smirnoff 	if (tp->t_flags2 & TF2_MBUF_QUEUE_READY)
5854f9c93f1SGleb Smirnoff 		should_wake = false;
5864f9c93f1SGleb Smirnoff 	else
5874f9c93f1SGleb Smirnoff 		should_wake = true;
5884f9c93f1SGleb Smirnoff 	/* Check if packets should be tapped to BPF. */
5894f9c93f1SGleb Smirnoff 	bpf_req = bpf_peers_present(lc->ifp->if_bpf);
5904f9c93f1SGleb Smirnoff 	lagg_bpf_req = false;
5914f9c93f1SGleb Smirnoff 	lagg_ifp = NULL;
5924f9c93f1SGleb Smirnoff 	if (lc->ifp->if_type == IFT_IEEE8023ADLAG ||
5934f9c93f1SGleb Smirnoff 	    lc->ifp->if_type == IFT_INFINIBANDLAG) {
5944f9c93f1SGleb Smirnoff 		struct lagg_port *lp = lc->ifp->if_lagg;
5954f9c93f1SGleb Smirnoff 		struct lagg_softc *sc = lp->lp_softc;
5964f9c93f1SGleb Smirnoff 
5974f9c93f1SGleb Smirnoff 		lagg_ifp = sc->sc_ifp;
5984f9c93f1SGleb Smirnoff 		if (lagg_ifp != NULL)
5994f9c93f1SGleb Smirnoff 			lagg_bpf_req = bpf_peers_present(lagg_ifp->if_bpf);
6004f9c93f1SGleb Smirnoff 	}
6014f9c93f1SGleb Smirnoff 
6024f9c93f1SGleb Smirnoff 	/* Strip and compress all the incoming packets. */
6034f9c93f1SGleb Smirnoff 	can_append_old_cmp = true;
6044f9c93f1SGleb Smirnoff 	cmp = NULL;
6054f9c93f1SGleb Smirnoff 	for (pp = &le->m_head; *pp != NULL; ) {
6064f9c93f1SGleb Smirnoff 		mv_to = NULL;
6074f9c93f1SGleb Smirnoff 		if (do_bpf_strip_and_compress(tp, lc, le, pp, &cmp, &mv_to,
6084f9c93f1SGleb Smirnoff 		    &should_wake, bpf_req, lagg_bpf_req, lagg_ifp,
6094f9c93f1SGleb Smirnoff 		    can_append_old_cmp) == false) {
6104f9c93f1SGleb Smirnoff 			/* Advance to next mbuf. */
6114f9c93f1SGleb Smirnoff 			pp = &(*pp)->m_nextpkt;
6124f9c93f1SGleb Smirnoff 			/*
6134f9c93f1SGleb Smirnoff 			 * Once we have appended we can't look in the pending
6144f9c93f1SGleb Smirnoff 			 * inbound packets for a compressed ack to append to.
6154f9c93f1SGleb Smirnoff 			 */
6164f9c93f1SGleb Smirnoff 			can_append_old_cmp = false;
6174f9c93f1SGleb Smirnoff 			/*
6184f9c93f1SGleb Smirnoff 			 * Once we append we also need to stop adding to any
6194f9c93f1SGleb Smirnoff 			 * compressed ack we were remembering. A new cmp
6204f9c93f1SGleb Smirnoff 			 * ack will be required.
6214f9c93f1SGleb Smirnoff 			 */
6224f9c93f1SGleb Smirnoff 			cmp = NULL;
623*8caa2f53SMichael Tuexen #ifdef TCP_BLACKBOX
6244f9c93f1SGleb Smirnoff 			tcp_lro_log(tp, lc, le, NULL, 25, 0, 0, 0, 0);
625*8caa2f53SMichael Tuexen #endif
6264f9c93f1SGleb Smirnoff 		} else if (mv_to != NULL) {
6274f9c93f1SGleb Smirnoff 			/* We are asked to move pp up */
6284f9c93f1SGleb Smirnoff 			pp = &mv_to->m_nextpkt;
629*8caa2f53SMichael Tuexen #ifdef TCP_BLACKBOX
6304f9c93f1SGleb Smirnoff 			tcp_lro_log(tp, lc, le, NULL, 24, 0, 0, 0, 0);
6314f9c93f1SGleb Smirnoff 		} else
6324f9c93f1SGleb Smirnoff 			tcp_lro_log(tp, lc, le, NULL, 26, 0, 0, 0, 0);
633*8caa2f53SMichael Tuexen #else
634*8caa2f53SMichael Tuexen 		}
635*8caa2f53SMichael Tuexen #endif
6364f9c93f1SGleb Smirnoff 	}
6374f9c93f1SGleb Smirnoff 	/* Update "m_last_mbuf", if any. */
6384f9c93f1SGleb Smirnoff 	if (pp == &le->m_head)
6394f9c93f1SGleb Smirnoff 		le->m_last_mbuf = *pp;
6404f9c93f1SGleb Smirnoff 	else
6414f9c93f1SGleb Smirnoff 		le->m_last_mbuf = __containerof(pp, struct mbuf, m_nextpkt);
6424f9c93f1SGleb Smirnoff 
6434f9c93f1SGleb Smirnoff 	/* Check if any data mbufs left. */
6444f9c93f1SGleb Smirnoff 	if (le->m_head != NULL) {
6454f9c93f1SGleb Smirnoff 		counter_u64_add(tcp_inp_lro_direct_queue, 1);
646*8caa2f53SMichael Tuexen #ifdef TCP_BLACKBOX
6474f9c93f1SGleb Smirnoff 		tcp_lro_log(tp, lc, le, NULL, 22, 1, tp->t_flags2, 0, 1);
648*8caa2f53SMichael Tuexen #endif
6494f9c93f1SGleb Smirnoff 		tcp_queue_pkts(tp, le);
6504f9c93f1SGleb Smirnoff 	}
6514f9c93f1SGleb Smirnoff 	if (should_wake) {
6524f9c93f1SGleb Smirnoff 		/* Wakeup */
6534f9c93f1SGleb Smirnoff 		counter_u64_add(tcp_inp_lro_wokeup_queue, 1);
6544f9c93f1SGleb Smirnoff 		if ((*tp->t_fb->tfb_do_queued_segments)(tp, 0))
6554f9c93f1SGleb Smirnoff 			/* TCP cb gone and unlocked. */
6564f9c93f1SGleb Smirnoff 			return (0);
6574f9c93f1SGleb Smirnoff 	}
6584f9c93f1SGleb Smirnoff 	INP_WUNLOCK(tptoinpcb(tp));
6594f9c93f1SGleb Smirnoff 
6604f9c93f1SGleb Smirnoff 	return (0);	/* Success. */
6614f9c93f1SGleb Smirnoff }
6622c6fc36aSGleb Smirnoff 
6632c6fc36aSGleb Smirnoff void
6642c6fc36aSGleb Smirnoff tcp_lro_hpts_init(void)
6652c6fc36aSGleb Smirnoff {
6662c6fc36aSGleb Smirnoff 	tcp_lro_flush_tcphpts = _tcp_lro_flush_tcphpts;
6672c6fc36aSGleb Smirnoff }
66848b55a7cSGleb Smirnoff 
66948b55a7cSGleb Smirnoff void
67048b55a7cSGleb Smirnoff tcp_lro_hpts_uninit(void)
67148b55a7cSGleb Smirnoff {
67248b55a7cSGleb Smirnoff 	atomic_store_ptr(&tcp_lro_flush_tcphpts, NULL);
67348b55a7cSGleb Smirnoff }
674