From: Alexander Bluhm Subject: TCP soft LRO timestamp optimization To: tech@openbsd.org Date: Wed, 7 May 2025 23:28:02 +0200 Hi, Instead of parsing all the TCP options, we consider only two cases. No options or only timestamps at aligned position. The latter is the common case which must be fast. We don't loose speed if we ignore packets with complicated options. In tcp_softlro_check() only packets without options or simple timestamps are considered for LRO. We enforce that timestamps are increasing to detect sequence number wraparounds. When we concatenate TCP packets, take the timestamps from the tail which are more recent. ok? bluhm Index: netinet/tcp_input.c =================================================================== RCS file: /data/mirror/openbsd/cvs/src/sys/netinet/tcp_input.c,v diff -u -p -r1.444 tcp_input.c --- netinet/tcp_input.c 7 May 2025 14:10:19 -0000 1.444 +++ netinet/tcp_input.c 7 May 2025 18:15:41 -0000 @@ -4335,6 +4335,19 @@ tcp_softlro_check(struct mbuf *m, struct if (!ISSET(ext->tcp->th_flags, TH_ACK)) return 0; + /* Either no TCP options or timestamp as in RFC 1323 appendix A. */ + if (ext->tcphlen > sizeof(struct tcphdr)) { + int optlen = ext->tcphlen - sizeof(struct tcphdr); + uint8_t *optp = (uint8_t *)(ext->tcp + 1); + + /* Same logic as in TCP input quick retrieval. */ + if ((optlen != TCPOLEN_TSTAMP_APPA && + (optlen <= TCPOLEN_TSTAMP_APPA || + optp[TCPOLEN_TSTAMP_APPA] != TCPOPT_EOL)) || + ((uint32_t *)optp)[0] != htonl(TCPOPT_TSTAMP_HDR)) + return 0; + } + return 1; } @@ -4401,36 +4414,18 @@ tcp_softlro(struct mbuf *mhead, struct e return 0; /* Ignore segments with different TCP options. */ - if (head->tcphlen - sizeof(struct tcphdr) != - tail->tcphlen - sizeof(struct tcphdr)) + if (head->tcphlen != tail->tcphlen) return 0; - /* Check for TCP options */ + /* TCP timestamp options must match, type and length already checked. */ if (head->tcphlen > sizeof(struct tcphdr)) { - char *hopt = (char *)(head->tcp) + sizeof(struct tcphdr); - char *topt = (char *)(tail->tcp) + sizeof(struct tcphdr); - int optsize = head->tcphlen - sizeof(struct tcphdr); - int optlen; - - for (; optsize > 0; optsize -= optlen) { - /* Ignore segments with different TCP options. */ - if (hopt[0] != topt[0] || hopt[1] != topt[1]) - return 0; - - /* Get option length */ - optlen = hopt[1]; - if (hopt[0] == TCPOPT_NOP) - optlen = 1; - else if (optlen < 2 || optlen > optsize) - return 0; /* Illegal length */ - - if (hopt[0] != TCPOPT_NOP && - hopt[0] != TCPOPT_TIMESTAMP) - return 0; /* Unsupported TCP option */ - - hopt += optlen; - topt += optlen; - } + uint32_t *hoptp = (uint32_t *)(head->tcp + 1); + uint32_t *toptp = (uint32_t *)(tail->tcp + 1); + + /* Tail timestamps must be more recent. */ + if (TSTMP_LT(ntohl(hoptp[1]), ntohl(toptp[1])) || + TSTMP_LT(ntohl(hoptp[2]), ntohl(toptp[2]))) + return 0; } /* Limit mbuf chain len to avoid m_defrag calls on forwarding. */ @@ -4460,6 +4455,15 @@ tcp_softlro(struct mbuf *mhead, struct e /* Adjust TCP header. */ head->tcp->th_win = tail->tcp->th_win; head->tcp->th_ack = tail->tcp->th_ack; + + /* Use more recent timestamps from tail. */ + if (head->tcphlen > sizeof(struct tcphdr)) { + uint32_t *hoptp = (uint32_t *)(head->tcp + 1); + uint32_t *toptp = (uint32_t *)(tail->tcp + 1); + + hoptp[1] = toptp[1]; + hoptp[2] = toptp[2]; + } /* Calculate header length of tail packet. */ hdrlen = sizeof(*tail->eh);