1*27f190a3SBjoern A. Zeeb /*- 2*27f190a3SBjoern A. Zeeb * Copyright (c) 2007, Myricom Inc. 3*27f190a3SBjoern A. Zeeb * Copyright (c) 2008, Intel Corporation. 4*27f190a3SBjoern A. Zeeb * All rights reserved. 5*27f190a3SBjoern A. Zeeb * 6*27f190a3SBjoern A. Zeeb * Redistribution and use in source and binary forms, with or without 7*27f190a3SBjoern A. Zeeb * modification, are permitted provided that the following conditions 8*27f190a3SBjoern A. Zeeb * are met: 9*27f190a3SBjoern A. Zeeb * 1. Redistributions of source code must retain the above copyright 10*27f190a3SBjoern A. Zeeb * notice, this list of conditions and the following disclaimer. 11*27f190a3SBjoern A. Zeeb * 2. Redistributions in binary form must reproduce the above copyright 12*27f190a3SBjoern A. Zeeb * notice, this list of conditions and the following disclaimer in the 13*27f190a3SBjoern A. Zeeb * documentation and/or other materials provided with the distribution. 14*27f190a3SBjoern A. Zeeb * 15*27f190a3SBjoern A. Zeeb * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16*27f190a3SBjoern A. Zeeb * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17*27f190a3SBjoern A. Zeeb * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18*27f190a3SBjoern A. Zeeb * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19*27f190a3SBjoern A. Zeeb * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20*27f190a3SBjoern A. Zeeb * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21*27f190a3SBjoern A. Zeeb * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22*27f190a3SBjoern A. Zeeb * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23*27f190a3SBjoern A. Zeeb * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24*27f190a3SBjoern A. Zeeb * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25*27f190a3SBjoern A. Zeeb * SUCH DAMAGE. 26*27f190a3SBjoern A. Zeeb * 27*27f190a3SBjoern A. Zeeb * $FreeBSD$ 28*27f190a3SBjoern A. Zeeb */ 296c5087a8SJack F Vogel 306c5087a8SJack F Vogel #include <sys/param.h> 316c5087a8SJack F Vogel #include <sys/systm.h> 326c5087a8SJack F Vogel #include <sys/endian.h> 336c5087a8SJack F Vogel #include <sys/mbuf.h> 346c5087a8SJack F Vogel #include <sys/kernel.h> 356c5087a8SJack F Vogel #include <sys/socket.h> 366c5087a8SJack F Vogel 376c5087a8SJack F Vogel #include <net/if.h> 386c5087a8SJack F Vogel #include <net/ethernet.h> 396c5087a8SJack F Vogel #include <net/if_media.h> 406c5087a8SJack F Vogel 416c5087a8SJack F Vogel #include <netinet/in_systm.h> 426c5087a8SJack F Vogel #include <netinet/in.h> 436c5087a8SJack F Vogel #include <netinet/ip.h> 446c5087a8SJack F Vogel #include <netinet/tcp.h> 456c5087a8SJack F Vogel #include <netinet/tcp_lro.h> 466c5087a8SJack F Vogel 476c5087a8SJack F Vogel #include <machine/bus.h> 486c5087a8SJack F Vogel #include <machine/in_cksum.h> 496c5087a8SJack F Vogel 506c5087a8SJack F Vogel 516c5087a8SJack F Vogel static uint16_t do_csum_data(uint16_t *raw, int len) 526c5087a8SJack F Vogel { 536c5087a8SJack F Vogel uint32_t csum; 546c5087a8SJack F Vogel csum = 0; 556c5087a8SJack F Vogel while (len > 0) { 566c5087a8SJack F Vogel csum += *raw; 576c5087a8SJack F Vogel raw++; 586c5087a8SJack F Vogel csum += *raw; 596c5087a8SJack F Vogel raw++; 606c5087a8SJack F Vogel len -= 4; 616c5087a8SJack F Vogel } 626c5087a8SJack F Vogel csum = (csum >> 16) + (csum & 0xffff); 636c5087a8SJack F Vogel csum = (csum >> 16) + (csum & 0xffff); 646c5087a8SJack F Vogel return (uint16_t)csum; 656c5087a8SJack F Vogel } 666c5087a8SJack F Vogel 676c5087a8SJack F Vogel /* 686c5087a8SJack F Vogel * Allocate and init the LRO data structures 696c5087a8SJack F Vogel */ 706c5087a8SJack F Vogel int 716c5087a8SJack F Vogel tcp_lro_init(struct lro_ctrl *cntl) 726c5087a8SJack F Vogel { 736c5087a8SJack F Vogel struct lro_entry *lro; 746c5087a8SJack F Vogel int i, error = 0; 756c5087a8SJack F Vogel 766c5087a8SJack F Vogel SLIST_INIT(&cntl->lro_free); 776c5087a8SJack F Vogel SLIST_INIT(&cntl->lro_active); 786c5087a8SJack F Vogel 796c5087a8SJack F Vogel cntl->lro_bad_csum = 0; 806c5087a8SJack F Vogel cntl->lro_queued = 0; 816c5087a8SJack F Vogel cntl->lro_flushed = 0; 826c5087a8SJack F Vogel 836c5087a8SJack F Vogel for (i = 0; i < LRO_ENTRIES; i++) { 846c5087a8SJack F Vogel lro = (struct lro_entry *) malloc(sizeof (struct lro_entry), 856c5087a8SJack F Vogel M_DEVBUF, M_NOWAIT | M_ZERO); 866c5087a8SJack F Vogel if (lro == NULL) { 876c5087a8SJack F Vogel if (i == 0) 886c5087a8SJack F Vogel error = ENOMEM; 896c5087a8SJack F Vogel break; 906c5087a8SJack F Vogel } 916c5087a8SJack F Vogel cntl->lro_cnt = i; 926c5087a8SJack F Vogel SLIST_INSERT_HEAD(&cntl->lro_free, lro, next); 936c5087a8SJack F Vogel } 946c5087a8SJack F Vogel 956c5087a8SJack F Vogel return (error); 966c5087a8SJack F Vogel } 976c5087a8SJack F Vogel 986c5087a8SJack F Vogel void 996c5087a8SJack F Vogel tcp_lro_free(struct lro_ctrl *cntl) 1006c5087a8SJack F Vogel { 1016c5087a8SJack F Vogel struct lro_entry *entry; 1026c5087a8SJack F Vogel 1036c5087a8SJack F Vogel while (!SLIST_EMPTY(&cntl->lro_free)) { 1046c5087a8SJack F Vogel entry = SLIST_FIRST(&cntl->lro_free); 1056c5087a8SJack F Vogel SLIST_REMOVE_HEAD(&cntl->lro_free, next); 1066c5087a8SJack F Vogel free(entry, M_DEVBUF); 1076c5087a8SJack F Vogel } 1086c5087a8SJack F Vogel } 1096c5087a8SJack F Vogel 1106c5087a8SJack F Vogel void 1116c5087a8SJack F Vogel tcp_lro_flush(struct lro_ctrl *cntl, struct lro_entry *lro) 1126c5087a8SJack F Vogel { 1136c5087a8SJack F Vogel struct ifnet *ifp; 1146c5087a8SJack F Vogel struct ip *ip; 1156c5087a8SJack F Vogel struct tcphdr *tcp; 1166c5087a8SJack F Vogel uint32_t *ts_ptr; 1176c5087a8SJack F Vogel uint32_t tcplen, tcp_csum; 1186c5087a8SJack F Vogel 1196c5087a8SJack F Vogel 1206c5087a8SJack F Vogel if (lro->append_cnt) { 1216c5087a8SJack F Vogel /* incorporate the new len into the ip header and 1226c5087a8SJack F Vogel * re-calculate the checksum */ 1236c5087a8SJack F Vogel ip = lro->ip; 1246c5087a8SJack F Vogel ip->ip_len = htons(lro->len - ETHER_HDR_LEN); 1256c5087a8SJack F Vogel ip->ip_sum = 0; 1266c5087a8SJack F Vogel ip->ip_sum = 0xffff ^ 1276c5087a8SJack F Vogel do_csum_data((uint16_t*)ip, 1286c5087a8SJack F Vogel sizeof (*ip)); 1296c5087a8SJack F Vogel 1306c5087a8SJack F Vogel lro->m_head->m_pkthdr.csum_flags = CSUM_IP_CHECKED | 1316c5087a8SJack F Vogel CSUM_IP_VALID | CSUM_DATA_VALID | CSUM_PSEUDO_HDR; 1326c5087a8SJack F Vogel lro->m_head->m_pkthdr.csum_data = 0xffff; 1336c5087a8SJack F Vogel lro->m_head->m_pkthdr.len = lro->len; 1346c5087a8SJack F Vogel 1356c5087a8SJack F Vogel /* incorporate the latest ack into the tcp header */ 1366c5087a8SJack F Vogel tcp = (struct tcphdr *) (ip + 1); 1376c5087a8SJack F Vogel tcp->th_ack = lro->ack_seq; 1386c5087a8SJack F Vogel tcp->th_win = lro->window; 1396c5087a8SJack F Vogel /* incorporate latest timestamp into the tcp header */ 1406c5087a8SJack F Vogel if (lro->timestamp) { 1416c5087a8SJack F Vogel ts_ptr = (uint32_t *)(tcp + 1); 1426c5087a8SJack F Vogel ts_ptr[1] = htonl(lro->tsval); 1436c5087a8SJack F Vogel ts_ptr[2] = lro->tsecr; 1446c5087a8SJack F Vogel } 1456c5087a8SJack F Vogel /* 1466c5087a8SJack F Vogel * update checksum in tcp header by re-calculating the 1476c5087a8SJack F Vogel * tcp pseudoheader checksum, and adding it to the checksum 1486c5087a8SJack F Vogel * of the tcp payload data 1496c5087a8SJack F Vogel */ 1506c5087a8SJack F Vogel tcp->th_sum = 0; 1516c5087a8SJack F Vogel tcplen = lro->len - sizeof(*ip) - ETHER_HDR_LEN; 1526c5087a8SJack F Vogel tcp_csum = lro->data_csum; 1536c5087a8SJack F Vogel tcp_csum += in_pseudo(ip->ip_src.s_addr, ip->ip_dst.s_addr, 1546c5087a8SJack F Vogel htons(tcplen + IPPROTO_TCP)); 1556c5087a8SJack F Vogel tcp_csum += do_csum_data((uint16_t*)tcp, 1566c5087a8SJack F Vogel tcp->th_off << 2); 1576c5087a8SJack F Vogel tcp_csum = (tcp_csum & 0xffff) + (tcp_csum >> 16); 1586c5087a8SJack F Vogel tcp_csum = (tcp_csum & 0xffff) + (tcp_csum >> 16); 1596c5087a8SJack F Vogel tcp->th_sum = 0xffff ^ tcp_csum; 1606c5087a8SJack F Vogel } 1616c5087a8SJack F Vogel ifp = cntl->ifp; 1626c5087a8SJack F Vogel (*ifp->if_input)(cntl->ifp, lro->m_head); 1636c5087a8SJack F Vogel cntl->lro_queued += lro->append_cnt + 1; 1646c5087a8SJack F Vogel cntl->lro_flushed++; 1656c5087a8SJack F Vogel lro->m_head = NULL; 1666c5087a8SJack F Vogel lro->timestamp = 0; 1676c5087a8SJack F Vogel lro->append_cnt = 0; 1686c5087a8SJack F Vogel SLIST_INSERT_HEAD(&cntl->lro_free, lro, next); 1696c5087a8SJack F Vogel } 1706c5087a8SJack F Vogel 1716c5087a8SJack F Vogel int 1726c5087a8SJack F Vogel tcp_lro_rx(struct lro_ctrl *cntl, struct mbuf *m_head, uint32_t csum) 1736c5087a8SJack F Vogel { 1746c5087a8SJack F Vogel struct ether_header *eh; 1756c5087a8SJack F Vogel struct ip *ip; 1766c5087a8SJack F Vogel struct tcphdr *tcp; 1776c5087a8SJack F Vogel uint32_t *ts_ptr; 1786c5087a8SJack F Vogel struct mbuf *m_nxt, *m_tail; 1796c5087a8SJack F Vogel struct lro_entry *lro; 1806c5087a8SJack F Vogel int hlen, ip_len, tcp_hdr_len, tcp_data_len, tot_len; 18145709593SKip Macy int opt_bytes, trim, csum_flags; 1826c5087a8SJack F Vogel uint32_t seq, tmp_csum, device_mtu; 1836c5087a8SJack F Vogel 1846c5087a8SJack F Vogel 1856c5087a8SJack F Vogel eh = mtod(m_head, struct ether_header *); 1866c5087a8SJack F Vogel if (eh->ether_type != htons(ETHERTYPE_IP)) 1876c5087a8SJack F Vogel return 1; 1886c5087a8SJack F Vogel ip = (struct ip *) (eh + 1); 1896c5087a8SJack F Vogel if (ip->ip_p != IPPROTO_TCP) 1906c5087a8SJack F Vogel return 1; 1916c5087a8SJack F Vogel 1926c5087a8SJack F Vogel /* ensure there are no options */ 1936c5087a8SJack F Vogel if ((ip->ip_hl << 2) != sizeof (*ip)) 1946c5087a8SJack F Vogel return -1; 1956c5087a8SJack F Vogel 1966c5087a8SJack F Vogel /* .. and the packet is not fragmented */ 1976c5087a8SJack F Vogel if (ip->ip_off & htons(IP_MF|IP_OFFMASK)) 1986c5087a8SJack F Vogel return -1; 1996c5087a8SJack F Vogel 2006c5087a8SJack F Vogel /* verify that the IP header checksum is correct */ 20145709593SKip Macy csum_flags = m_head->m_pkthdr.csum_flags; 20245709593SKip Macy if (csum_flags & CSUM_IP_CHECKED) { 20345709593SKip Macy if (__predict_false((csum_flags & CSUM_IP_VALID) == 0)) { 20445709593SKip Macy cntl->lro_bad_csum++; 20545709593SKip Macy return -1; 20645709593SKip Macy } 20745709593SKip Macy } else { 2086c5087a8SJack F Vogel tmp_csum = do_csum_data((uint16_t *)ip, sizeof (*ip)); 2096c5087a8SJack F Vogel if (__predict_false((tmp_csum ^ 0xffff) != 0)) { 2106c5087a8SJack F Vogel cntl->lro_bad_csum++; 2116c5087a8SJack F Vogel return -1; 2126c5087a8SJack F Vogel } 21345709593SKip Macy } 2146c5087a8SJack F Vogel 2156c5087a8SJack F Vogel /* find the TCP header */ 2166c5087a8SJack F Vogel tcp = (struct tcphdr *) (ip + 1); 2176c5087a8SJack F Vogel 2186c5087a8SJack F Vogel /* Get the TCP checksum if we dont have it */ 2196c5087a8SJack F Vogel if (!csum) 2206c5087a8SJack F Vogel csum = tcp->th_sum; 2216c5087a8SJack F Vogel 2226c5087a8SJack F Vogel /* ensure no bits set besides ack or psh */ 2236c5087a8SJack F Vogel if ((tcp->th_flags & ~(TH_ACK | TH_PUSH)) != 0) 2246c5087a8SJack F Vogel return -1; 2256c5087a8SJack F Vogel 2266c5087a8SJack F Vogel /* check for timestamps. Since the only option we handle are 2276c5087a8SJack F Vogel timestamps, we only have to handle the simple case of 2286c5087a8SJack F Vogel aligned timestamps */ 2296c5087a8SJack F Vogel 2306c5087a8SJack F Vogel opt_bytes = (tcp->th_off << 2) - sizeof (*tcp); 2316c5087a8SJack F Vogel tcp_hdr_len = sizeof (*tcp) + opt_bytes; 2326c5087a8SJack F Vogel ts_ptr = (uint32_t *)(tcp + 1); 2336c5087a8SJack F Vogel if (opt_bytes != 0) { 2346c5087a8SJack F Vogel if (__predict_false(opt_bytes != TCPOLEN_TSTAMP_APPA) || 2356c5087a8SJack F Vogel (*ts_ptr != ntohl(TCPOPT_NOP<<24|TCPOPT_NOP<<16| 2366c5087a8SJack F Vogel TCPOPT_TIMESTAMP<<8|TCPOLEN_TIMESTAMP))) 2376c5087a8SJack F Vogel return -1; 2386c5087a8SJack F Vogel } 2396c5087a8SJack F Vogel 2406c5087a8SJack F Vogel ip_len = ntohs(ip->ip_len); 2416c5087a8SJack F Vogel tcp_data_len = ip_len - (tcp->th_off << 2) - sizeof (*ip); 2426c5087a8SJack F Vogel 2436c5087a8SJack F Vogel 2446c5087a8SJack F Vogel /* 2456c5087a8SJack F Vogel * If frame is padded beyond the end of the IP packet, 2466c5087a8SJack F Vogel * then we must trim the extra bytes off the end. 2476c5087a8SJack F Vogel */ 2486c5087a8SJack F Vogel tot_len = m_head->m_pkthdr.len; 2496c5087a8SJack F Vogel trim = tot_len - (ip_len + ETHER_HDR_LEN); 2506c5087a8SJack F Vogel if (trim != 0) { 2516c5087a8SJack F Vogel if (trim < 0) { 2526c5087a8SJack F Vogel /* truncated packet */ 2536c5087a8SJack F Vogel return -1; 2546c5087a8SJack F Vogel } 2556c5087a8SJack F Vogel m_adj(m_head, -trim); 2566c5087a8SJack F Vogel tot_len = m_head->m_pkthdr.len; 2576c5087a8SJack F Vogel } 2586c5087a8SJack F Vogel 2596c5087a8SJack F Vogel m_nxt = m_head; 2606c5087a8SJack F Vogel m_tail = NULL; /* -Wuninitialized */ 2616c5087a8SJack F Vogel while (m_nxt != NULL) { 2626c5087a8SJack F Vogel m_tail = m_nxt; 2636c5087a8SJack F Vogel m_nxt = m_tail->m_next; 2646c5087a8SJack F Vogel } 2656c5087a8SJack F Vogel 2666c5087a8SJack F Vogel hlen = ip_len + ETHER_HDR_LEN - tcp_data_len; 2676c5087a8SJack F Vogel seq = ntohl(tcp->th_seq); 2686c5087a8SJack F Vogel 2696c5087a8SJack F Vogel SLIST_FOREACH(lro, &cntl->lro_active, next) { 2706c5087a8SJack F Vogel if (lro->source_port == tcp->th_sport && 2716c5087a8SJack F Vogel lro->dest_port == tcp->th_dport && 2726c5087a8SJack F Vogel lro->source_ip == ip->ip_src.s_addr && 2736c5087a8SJack F Vogel lro->dest_ip == ip->ip_dst.s_addr) { 274ca712262SColin Percival /* Flush now if appending will result in overflow. */ 275ca712262SColin Percival if (lro->len > (65535 - tcp_data_len)) { 276ca712262SColin Percival SLIST_REMOVE(&cntl->lro_active, lro, 277ca712262SColin Percival lro_entry, next); 278ca712262SColin Percival tcp_lro_flush(cntl, lro); 279ca712262SColin Percival break; 280ca712262SColin Percival } 281ca712262SColin Percival 2826c5087a8SJack F Vogel /* Try to append it */ 2836c5087a8SJack F Vogel 284c31aa19cSJack F Vogel if (__predict_false(seq != lro->next_seq || 285c31aa19cSJack F Vogel (tcp_data_len == 0 && 286c31aa19cSJack F Vogel lro->ack_seq == tcp->th_ack))) { 287c31aa19cSJack F Vogel /* out of order packet or dup ack */ 2886c5087a8SJack F Vogel SLIST_REMOVE(&cntl->lro_active, lro, 2896c5087a8SJack F Vogel lro_entry, next); 2906c5087a8SJack F Vogel tcp_lro_flush(cntl, lro); 2916c5087a8SJack F Vogel return -1; 2926c5087a8SJack F Vogel } 2936c5087a8SJack F Vogel 2946c5087a8SJack F Vogel if (opt_bytes) { 2956c5087a8SJack F Vogel uint32_t tsval = ntohl(*(ts_ptr + 1)); 2966c5087a8SJack F Vogel /* make sure timestamp values are increasing */ 2976c5087a8SJack F Vogel if (__predict_false(lro->tsval > tsval || 2986c5087a8SJack F Vogel *(ts_ptr + 2) == 0)) { 2996c5087a8SJack F Vogel return -1; 3006c5087a8SJack F Vogel } 3016c5087a8SJack F Vogel lro->tsval = tsval; 3026c5087a8SJack F Vogel lro->tsecr = *(ts_ptr + 2); 3036c5087a8SJack F Vogel } 3046c5087a8SJack F Vogel 3056c5087a8SJack F Vogel lro->next_seq += tcp_data_len; 3066c5087a8SJack F Vogel lro->ack_seq = tcp->th_ack; 3076c5087a8SJack F Vogel lro->window = tcp->th_win; 3086c5087a8SJack F Vogel lro->append_cnt++; 3096c5087a8SJack F Vogel if (tcp_data_len == 0) { 3106c5087a8SJack F Vogel m_freem(m_head); 3116c5087a8SJack F Vogel return 0; 3126c5087a8SJack F Vogel } 3136c5087a8SJack F Vogel /* subtract off the checksum of the tcp header 3146c5087a8SJack F Vogel * from the hardware checksum, and add it to the 3156c5087a8SJack F Vogel * stored tcp data checksum. Byteswap the checksum 3166c5087a8SJack F Vogel * if the total length so far is odd 3176c5087a8SJack F Vogel */ 3186c5087a8SJack F Vogel tmp_csum = do_csum_data((uint16_t*)tcp, 3196c5087a8SJack F Vogel tcp_hdr_len); 3206c5087a8SJack F Vogel csum = csum + (tmp_csum ^ 0xffff); 3216c5087a8SJack F Vogel csum = (csum & 0xffff) + (csum >> 16); 3226c5087a8SJack F Vogel csum = (csum & 0xffff) + (csum >> 16); 3236c5087a8SJack F Vogel if (lro->len & 0x1) { 3246c5087a8SJack F Vogel /* Odd number of bytes so far, flip bytes */ 3256c5087a8SJack F Vogel csum = ((csum << 8) | (csum >> 8)) & 0xffff; 3266c5087a8SJack F Vogel } 3276c5087a8SJack F Vogel csum = csum + lro->data_csum; 3286c5087a8SJack F Vogel csum = (csum & 0xffff) + (csum >> 16); 3296c5087a8SJack F Vogel csum = (csum & 0xffff) + (csum >> 16); 3306c5087a8SJack F Vogel lro->data_csum = csum; 3316c5087a8SJack F Vogel 3326c5087a8SJack F Vogel lro->len += tcp_data_len; 3336c5087a8SJack F Vogel 3346c5087a8SJack F Vogel /* adjust mbuf so that m->m_data points to 3356c5087a8SJack F Vogel the first byte of the payload */ 3366c5087a8SJack F Vogel m_adj(m_head, hlen); 3376c5087a8SJack F Vogel /* append mbuf chain */ 3386c5087a8SJack F Vogel lro->m_tail->m_next = m_head; 3396c5087a8SJack F Vogel /* advance the last pointer */ 3406c5087a8SJack F Vogel lro->m_tail = m_tail; 3416c5087a8SJack F Vogel /* flush packet if required */ 3426c5087a8SJack F Vogel device_mtu = cntl->ifp->if_mtu; 3436c5087a8SJack F Vogel if (lro->len > (65535 - device_mtu)) { 3446c5087a8SJack F Vogel SLIST_REMOVE(&cntl->lro_active, lro, 3456c5087a8SJack F Vogel lro_entry, next); 3466c5087a8SJack F Vogel tcp_lro_flush(cntl, lro); 3476c5087a8SJack F Vogel } 3486c5087a8SJack F Vogel return 0; 3496c5087a8SJack F Vogel } 3506c5087a8SJack F Vogel } 3516c5087a8SJack F Vogel 3526c5087a8SJack F Vogel if (SLIST_EMPTY(&cntl->lro_free)) 3536c5087a8SJack F Vogel return -1; 3546c5087a8SJack F Vogel 3556c5087a8SJack F Vogel /* start a new chain */ 3566c5087a8SJack F Vogel lro = SLIST_FIRST(&cntl->lro_free); 3576c5087a8SJack F Vogel SLIST_REMOVE_HEAD(&cntl->lro_free, next); 3586c5087a8SJack F Vogel SLIST_INSERT_HEAD(&cntl->lro_active, lro, next); 3596c5087a8SJack F Vogel lro->source_port = tcp->th_sport; 3606c5087a8SJack F Vogel lro->dest_port = tcp->th_dport; 3616c5087a8SJack F Vogel lro->source_ip = ip->ip_src.s_addr; 3626c5087a8SJack F Vogel lro->dest_ip = ip->ip_dst.s_addr; 3636c5087a8SJack F Vogel lro->next_seq = seq + tcp_data_len; 3646c5087a8SJack F Vogel lro->mss = tcp_data_len; 3656c5087a8SJack F Vogel lro->ack_seq = tcp->th_ack; 3666c5087a8SJack F Vogel lro->window = tcp->th_win; 3676c5087a8SJack F Vogel 3686c5087a8SJack F Vogel /* save the checksum of just the TCP payload by 3696c5087a8SJack F Vogel * subtracting off the checksum of the TCP header from 3706c5087a8SJack F Vogel * the entire hardware checksum 3716c5087a8SJack F Vogel * Since IP header checksum is correct, checksum over 3726c5087a8SJack F Vogel * the IP header is -0. Substracting -0 is unnecessary. 3736c5087a8SJack F Vogel */ 3746c5087a8SJack F Vogel tmp_csum = do_csum_data((uint16_t*)tcp, tcp_hdr_len); 3756c5087a8SJack F Vogel csum = csum + (tmp_csum ^ 0xffff); 3766c5087a8SJack F Vogel csum = (csum & 0xffff) + (csum >> 16); 3776c5087a8SJack F Vogel csum = (csum & 0xffff) + (csum >> 16); 3786c5087a8SJack F Vogel lro->data_csum = csum; 3796c5087a8SJack F Vogel 3806c5087a8SJack F Vogel lro->ip = ip; 3816c5087a8SJack F Vogel /* record timestamp if it is present */ 3826c5087a8SJack F Vogel if (opt_bytes) { 3836c5087a8SJack F Vogel lro->timestamp = 1; 3846c5087a8SJack F Vogel lro->tsval = ntohl(*(ts_ptr + 1)); 3856c5087a8SJack F Vogel lro->tsecr = *(ts_ptr + 2); 3866c5087a8SJack F Vogel } 3876c5087a8SJack F Vogel lro->len = tot_len; 3886c5087a8SJack F Vogel lro->m_head = m_head; 3896c5087a8SJack F Vogel lro->m_tail = m_tail; 3906c5087a8SJack F Vogel return 0; 3916c5087a8SJack F Vogel } 392