From 015f2bb82ec40e5c966a1e3d64603d2d7f7687d7 Mon Sep 17 00:00:00 2001 From: Mark Pizzolato Date: Fri, 12 Aug 2011 17:53:32 -0700 Subject: [PATCH] Fixed race condition detecting reflections when threaded reading and writing is enabled. Fixed handling of Jumbo Packets and LSO (Large Send Offload) behaviorst to: 1) Avoid truncation of very large sends 2) handle the case where the host network stack may not populate the IP header length for a large send. --- sim_ether.c | 86 +++++++++++++++++++++++++++++++++++++---------------- sim_ether.h | 5 ++-- 2 files changed, 63 insertions(+), 28 deletions(-) diff --git a/sim_ether.c b/sim_ether.c index 50fe7925..525f48bc 100644 --- a/sim_ether.c +++ b/sim_ether.c @@ -1069,7 +1069,7 @@ static int pcap_mac_if_win32(char *AdapterName, UCHAR MACAddress[6]) OidData->Length = 6; memset(OidData->Data, 0, 6); - + Status = p_PacketRequest(lpAdapter, FALSE, OidData); if(Status) { memcpy(MACAddress, OidData->Data, 6); @@ -1203,7 +1203,7 @@ else len = read(dev->fd_handle, buf, sizeof(buf)); if (len > 0) { status = 1; - header.len = len; + header.caplen = header.len = len; _eth_callback((u_char *)dev, &header, buf); } else { status = 0; @@ -1611,8 +1611,22 @@ t_stat _eth_write(ETH_DEV* dev, ETH_PACK* packet, ETH_PCALLBACK routine) /* make sure packet is acceptable length */ if ((packet->len >= ETH_MIN_PACKET) && (packet->len <= ETH_MAX_PACKET)) { + int loopback_self_frame = LOOPBACK_SELF_FRAME(dev->physical_addr, packet->msg); + eth_packet_trace (dev, packet->msg, packet->len, "writing"); + /* record sending of loopback packet (done before actual send to avoid race conditions with receiver) */ + if (loopback_self_frame) { +#ifdef USE_READER_THREAD + pthread_mutex_lock (&dev->self_lock); +#endif + dev->loopback_self_sent += dev->reflections; + dev->loopback_self_sent_total++; +#ifdef USE_READER_THREAD + pthread_mutex_unlock (&dev->self_lock); +#endif + } + /* dispatch write request (synchronous; no need to save write info to dev) */ if (dev->pcap_mode) status = pcap_sendpacket((pcap_t*)dev->handle, (u_char*)packet->msg, packet->len); @@ -1621,13 +1635,13 @@ t_stat _eth_write(ETH_DEV* dev, ETH_PACK* packet, ETH_PCALLBACK routine) status = ((packet->len == write(dev->fd_handle, (void *)packet->msg, packet->len)) ? 0 : -1); #endif - /* detect sending of loopback packet */ - if ((status == 0) && (LOOPBACK_SELF_FRAME(dev->physical_addr, packet->msg))) { + /* On error, correct loopback bookkeeping */ + if ((status != 0) && loopback_self_frame) { #ifdef USE_READER_THREAD pthread_mutex_lock (&dev->self_lock); #endif - dev->loopback_self_sent += dev->reflections; - dev->loopback_self_sent_total++; + dev->loopback_self_sent -= dev->reflections; + dev->loopback_self_sent_total--; #ifdef USE_READER_THREAD pthread_mutex_unlock (&dev->self_lock); #endif @@ -1808,7 +1822,7 @@ struct TCPHeader { #define TCP_RST_FLAG (0x04) #define TCP_SYN_FLAG (0x02) #define TCP_FIN_FLAG (0x01) -#define TCP_FLAGS_MASK (0xFF) +#define TCP_FLAGS_MASK (0xFFF) uint16 window; uint16 checksum; uint16 urgent; @@ -1889,7 +1903,7 @@ _eth_fix_ip_jumbo_offload(ETH_DEV* dev, const u_char* msg, int len) uint16 ip_flags; uint16 frag_offset; struct pcap_pkthdr header; - uint16 tcp_flags; + uint16 orig_tcp_flags; /* Only interested in IP frames */ if (ntohs(*proto) != 0x0800) { @@ -1917,7 +1931,7 @@ _eth_fix_ip_jumbo_offload(ETH_DEV* dev, const u_char* msg, int len) return; } if (UDP->checksum == 0) - break; /* UDP Cghecksums are disabled */ + break; /* UDP Checksums are disabled */ orig_checksum = UDP->checksum; UDP->checksum = 0; UDP->checksum = pseudo_checksum(ntohs(UDP->length), IPPROTO_UDP, (uint16 *)(&IP->source_ip), (uint16 *)(&IP->dest_ip), (uint8 *)UDP); @@ -1971,10 +1985,16 @@ _eth_fix_ip_jumbo_offload(ETH_DEV* dev, const u_char* msg, int len) IP->flags = htons(ip_flags); IP->checksum = 0; IP->checksum = ip_checksum((uint16 *)IP, IP_HLEN(IP)); - header.len = 14 + ntohs(IP->total_len); + header.caplen = header.len = 14 + ntohs(IP->total_len); eth_packet_trace (dev, ((u_char *)IP)-14, header.len, "reading Datagram fragment"); #if ETH_MIN_JUMBO_FRAME < ETH_MAX_PACKET - { /* Debugging is easier it we read packets directly with pcap */ + { /* Debugging is easier if we read packets directly with pcap + (i.e. we can use Wireshark to verify packet contents) + we don't want to do this all the time for 2 reasons: + 1) sending through pcap involves kernel transitions and + 2) if the current system reflects sent packets, the + recieving side will receive and process 2 copies of + any packets sent this way. */ ETH_PACK pkt; memset(&pkt, 0, sizeof(pkt)); @@ -1996,26 +2016,35 @@ _eth_fix_ip_jumbo_offload(ETH_DEV* dev, const u_char* msg, int len) break; case IPPROTO_TCP: ++dev->jumbo_fragmented; + eth_packet_trace_ex (dev, ((u_char *)IP)-14, len, "Fragmenting Jumbo TCP segment", 1, dev->dbit); TCP = (struct TCPHeader *)(((char *)IP)+IP_HLEN(IP)); - tcp_flags = ntohs(TCP->data_offset_and_flags)&TCP_FLAGS_MASK; - TCP->data_offset_and_flags = htons(((TCP_DATA_OFFSET(TCP)>>2)<<12)|TCP_ACK_FLAG); - payload_len = ntohs(IP->total_len) - IP_HLEN(IP) - TCP_DATA_OFFSET(TCP); + orig_tcp_flags = ntohs(TCP->data_offset_and_flags); + if (0 == ntohs(IP->total_len)) /* Sometimes the IP header indicates a packet size of 0 */ + IP->total_len = htons(len-14); /* use the captured frame size to compute the IP length */ + payload_len = ntohs(IP->total_len) - (IP_HLEN(IP) + TCP_DATA_OFFSET(TCP)); mtu_payload = ETH_MIN_JUMBO_FRAME - 14 - IP_HLEN(IP) - TCP_DATA_OFFSET(TCP); while (payload_len > 0) { if (payload_len > mtu_payload) { + TCP->data_offset_and_flags = htons(orig_tcp_flags&~(TCP_PSH_FLAG|TCP_FIN_FLAG|TCP_RST_FLAG)); IP->total_len = htons(mtu_payload + IP_HLEN(IP) + TCP_DATA_OFFSET(TCP)); } else { - TCP->data_offset_and_flags = htons(ntohs(TCP->data_offset_and_flags)|(tcp_flags&(TCP_PSH_FLAG|TCP_ACK_FLAG|TCP_FIN_FLAG|TCP_RST_FLAG))); + TCP->data_offset_and_flags = htons(orig_tcp_flags); IP->total_len = htons(payload_len + IP_HLEN(IP) + TCP_DATA_OFFSET(TCP)); } IP->checksum = 0; IP->checksum = ip_checksum((uint16 *)IP, IP_HLEN(IP)); TCP->checksum = 0; TCP->checksum = pseudo_checksum(ntohs(IP->total_len)-IP_HLEN(IP), IPPROTO_TCP, (uint16 *)(&IP->source_ip), (uint16 *)(&IP->dest_ip), (uint8 *)TCP); - header.len = 14 + ntohs(IP->total_len); + header.caplen = header.len = 14 + ntohs(IP->total_len); eth_packet_trace_ex (dev, ((u_char *)IP)-14, header.len, "reading TCP segment", 1, dev->dbit); #if ETH_MIN_JUMBO_FRAME < ETH_MAX_PACKET - { /* Debugging is easier it we read packets directly with pcap */ + { /* Debugging is easier if we read packets directly with pcap + (i.e. we can use Wireshark to verify packet contents) + we don't want to do this all the time for 2 reasons: + 1) sending through pcap involves kernel transitions and + 2) if the current system reflects sent packets, the + recieving side will receive and process 2 copies of + any packets sent this way. */ ETH_PACK pkt; memset(&pkt, 0, sizeof(pkt)); @@ -2073,7 +2102,7 @@ _eth_fix_ip_xsum_offload(ETH_DEV* dev, u_char* msg, int len) if (ntohs(UDP->length) > (len-IP_HLEN(IP))) return; /* packet contained length exceeds packet size */ if (UDP->checksum == 0) - return; /* UDP Cghecksums are disabled */ + return; /* UDP Checksums are disabled */ orig_checksum = UDP->checksum; UDP->checksum = 0; UDP->checksum = pseudo_checksum(ntohs(UDP->length), IPPROTO_UDP, (uint16 *)(&IP->source_ip), (uint16 *)(&IP->dest_ip), (uint8 *)UDP); @@ -2115,7 +2144,7 @@ _eth_callback(u_char* info, const struct pcap_pkthdr* header, const u_char* data int i; eth_packet_trace (dev, data, header->len, "received"); - +f for (i = 0; i < dev->addr_count; i++) { if (memcmp(data, dev->filter_address[i], 6) == 0) to_me = 1; if (memcmp(&data[6], dev->filter_address[i], 6) == 0) from_me = 1; @@ -2159,7 +2188,10 @@ _eth_callback(u_char* info, const struct pcap_pkthdr* header, const u_char* data if (to_me && !from_me) { #endif if (header->len > ETH_MIN_JUMBO_FRAME) { - _eth_fix_ip_jumbo_offload(dev, data, header->len); + if (header->len <= header->caplen) /* Whole Frame captured? */ + _eth_fix_ip_jumbo_offload(dev, data, header->len); + else + ++dev->jumbo_truncated; return; } #if defined (USE_READER_THREAD) @@ -2255,7 +2287,7 @@ int eth_read(ETH_DEV* dev, ETH_PACK* packet, ETH_PCALLBACK routine) len = read(dev->fd_handle, buf, sizeof(buf)); if (len > 0) { status = 1; - header.len = len; + header.caplen = header.len = len; _eth_callback((u_char *)dev, &header, buf); } else { status = 0; @@ -2435,11 +2467,6 @@ t_stat eth_filter_hash(ETH_DEV* dev, int addr_count, ETH_MAC* const addresses, #ifdef USE_BPF if (dev->pcap_mode) { -#ifdef USE_READER_THREAD - pthread_mutex_lock (&dev->lock); - ethq_clear (&dev->read_queue); /* Empty FIFO Queue when filter list changes */ - pthread_mutex_unlock (&dev->lock); -#endif /* compile filter string */ if ((status = pcap_compile(dev->handle, &bpf, buf, 1, bpf_netmask)) < 0) { sprintf(errbuf, "%s", pcap_geterr(dev->handle)); @@ -2467,6 +2494,11 @@ t_stat eth_filter_hash(ETH_DEV* dev, int addr_count, ETH_MAC* const addresses, } pcap_freecode(&bpf); } +#ifdef USE_READER_THREAD + pthread_mutex_lock (&dev->lock); + ethq_clear (&dev->read_queue); /* Empty FIFO Queue when filter list changes */ + pthread_mutex_unlock (&dev->lock); +#endif } #endif /* USE_BPF */ @@ -2627,6 +2659,8 @@ void eth_show_dev (FILE *st, ETH_DEV* dev) fprintf(st, " Jumbo Dropped: %d\n", dev->jumbo_dropped); if (dev->jumbo_fragmented) fprintf(st, " Jumbo Fragmented: %d\n", dev->jumbo_fragmented); + if (dev->jumbo_truncated) + fprintf(st, " Jumbo Truncated: %d\n", dev->jumbo_truncated); #if defined(USE_READER_THREAD) fprintf(st, " Asynch Interrupts: %s\n", dev->asynch_io?"Enabled":"Disabled"); if (dev->asynch_io) diff --git a/sim_ether.h b/sim_ether.h index c9817788..de155548 100644 --- a/sim_ether.h +++ b/sim_ether.h @@ -118,7 +118,7 @@ #define ETH_DEV_DESC_MAX 256 /* maximum device description size */ #define ETH_MIN_PACKET 60 /* minimum ethernet packet size */ #define ETH_MAX_PACKET 1514 /* maximum ethernet packet size */ -#define ETH_MAX_JUMBO_FRAME 16384 /* maximum ethernet jumbo frame size */ +#define ETH_MAX_JUMBO_FRAME 65536 /* maximum ethernet jumbo frame size (or Offload Segment Size) */ #define ETH_MAX_DEVICE 10 /* maximum ethernet devices */ #define ETH_CRC_SIZE 4 /* ethernet CRC size */ #define ETH_FRAME_SIZE (ETH_MAX_PACKET+ETH_CRC_SIZE) /* ethernet maximum frame size */ @@ -193,10 +193,11 @@ struct eth_device { ETH_MAC host_nic_phy_hw_addr; /* MAC address of the attached NIC */ uint32 jumbo_fragmented; /* Giant IPv4 Frames Fragmented */ uint32 jumbo_dropped; /* Giant Frames Dropped */ + uint32 jumbo_truncated; /* Giant Frames too big for capture buffer - Dropped */ DEVICE* dptr; /* device ethernet is attached to */ uint32 dbit; /* debugging bit */ int reflections; /* packet reflections on interface */ - int need_crc; /* device needs CRC (Cyclic Redundancy Check) */ + int need_crc; /* device needs CRC (Cyclic Redundancy Check) */ #if defined (USE_READER_THREAD) int asynch_io; /* Asynchronous Interrupt scheduling enabled */ int asynch_io_latency; /* instructions to delay pending interrupt */