mirror of https://gitee.com/openkylin/qemu.git
Pull request
-----BEGIN PGP SIGNATURE----- Version: GnuPG v1 iQEcBAABAgAGBQJVv1m5AAoJEJykq7OBq3PIO+QIAMbPSu/ZqfdANX+H4bgshudw 9CMYsgEqlmjOSeFP1Pp7lYbuceInsvY4Ks+GtWsPkV/mhEl4+g1h9uWKMSWg0jsU 14hfi7ibJomFngRPkEhcDemu6JSLAGsqedLPyrFZZyGzVZnY/TmpwG3s9CiSUSU0 h7knQLxt8QemsPU+rlH6xE/QkSdyWpERsUCTpcKufIGwIZJDeUfW1/9UxDp6M0QK LZj+8ZJzF2g/s51xHCTmAyvFZxROceEgUbGyWYNh1Aj55LMT7k+t4TTEYkizqTYj n0AUDzV65Pm67OWRf22siqb74BPt72gf/048LmhqE7o4NZvLqJ7Kff6pFDYOF0Q= =9hkg -----END PGP SIGNATURE----- Merge remote-tracking branch 'remotes/stefanha/tags/rtl8139-cplus-tx-input-validation-pull-request' into staging Pull request # gpg: Signature made Mon Aug 3 13:08:25 2015 BST using RSA key ID 81AB73C8 # gpg: Good signature from "Stefan Hajnoczi <stefanha@redhat.com>" # gpg: aka "Stefan Hajnoczi <stefanha@gmail.com>" * remotes/stefanha/tags/rtl8139-cplus-tx-input-validation-pull-request: rtl8139: check TCP Data Offset field (CVE-2015-5165) rtl8139: skip offload on short TCP header (CVE-2015-5165) rtl8139: check IP Total Length field (CVE-2015-5165) rtl8139: check IP Header Length field (CVE-2015-5165) rtl8139: skip offload on short Ethernet/IP header (CVE-2015-5165) rtl8139: drop tautologous if (ip) {...} statement (CVE-2015-5165) rtl8139: avoid nested ifs in IP header parsing (CVE-2015-5165) Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
commit
2a3612ccc1
395
hw/net/rtl8139.c
395
hw/net/rtl8139.c
|
@ -2150,6 +2150,11 @@ static int rtl8139_cplus_transmit_one(RTL8139State *s)
|
||||||
{
|
{
|
||||||
DPRINTF("+++ C+ mode offloaded task checksum\n");
|
DPRINTF("+++ C+ mode offloaded task checksum\n");
|
||||||
|
|
||||||
|
/* Large enough for Ethernet and IP headers? */
|
||||||
|
if (saved_size < ETH_HLEN + sizeof(ip_header)) {
|
||||||
|
goto skip_offload;
|
||||||
|
}
|
||||||
|
|
||||||
/* ip packet header */
|
/* ip packet header */
|
||||||
ip_header *ip = NULL;
|
ip_header *ip = NULL;
|
||||||
int hlen = 0;
|
int hlen = 0;
|
||||||
|
@ -2160,223 +2165,235 @@ static int rtl8139_cplus_transmit_one(RTL8139State *s)
|
||||||
size_t eth_payload_len = 0;
|
size_t eth_payload_len = 0;
|
||||||
|
|
||||||
int proto = be16_to_cpu(*(uint16_t *)(saved_buffer + 12));
|
int proto = be16_to_cpu(*(uint16_t *)(saved_buffer + 12));
|
||||||
if (proto == ETH_P_IP)
|
if (proto != ETH_P_IP)
|
||||||
{
|
{
|
||||||
DPRINTF("+++ C+ mode has IP packet\n");
|
goto skip_offload;
|
||||||
|
|
||||||
/* not aligned */
|
|
||||||
eth_payload_data = saved_buffer + ETH_HLEN;
|
|
||||||
eth_payload_len = saved_size - ETH_HLEN;
|
|
||||||
|
|
||||||
ip = (ip_header*)eth_payload_data;
|
|
||||||
|
|
||||||
if (IP_HEADER_VERSION(ip) != IP_HEADER_VERSION_4) {
|
|
||||||
DPRINTF("+++ C+ mode packet has bad IP version %d "
|
|
||||||
"expected %d\n", IP_HEADER_VERSION(ip),
|
|
||||||
IP_HEADER_VERSION_4);
|
|
||||||
ip = NULL;
|
|
||||||
} else {
|
|
||||||
hlen = IP_HEADER_LENGTH(ip);
|
|
||||||
ip_protocol = ip->ip_p;
|
|
||||||
ip_data_len = be16_to_cpu(ip->ip_len) - hlen;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ip)
|
DPRINTF("+++ C+ mode has IP packet\n");
|
||||||
|
|
||||||
|
/* not aligned */
|
||||||
|
eth_payload_data = saved_buffer + ETH_HLEN;
|
||||||
|
eth_payload_len = saved_size - ETH_HLEN;
|
||||||
|
|
||||||
|
ip = (ip_header*)eth_payload_data;
|
||||||
|
|
||||||
|
if (IP_HEADER_VERSION(ip) != IP_HEADER_VERSION_4) {
|
||||||
|
DPRINTF("+++ C+ mode packet has bad IP version %d "
|
||||||
|
"expected %d\n", IP_HEADER_VERSION(ip),
|
||||||
|
IP_HEADER_VERSION_4);
|
||||||
|
goto skip_offload;
|
||||||
|
}
|
||||||
|
|
||||||
|
hlen = IP_HEADER_LENGTH(ip);
|
||||||
|
if (hlen < sizeof(ip_header) || hlen > eth_payload_len) {
|
||||||
|
goto skip_offload;
|
||||||
|
}
|
||||||
|
|
||||||
|
ip_protocol = ip->ip_p;
|
||||||
|
|
||||||
|
ip_data_len = be16_to_cpu(ip->ip_len);
|
||||||
|
if (ip_data_len < hlen || ip_data_len > eth_payload_len) {
|
||||||
|
goto skip_offload;
|
||||||
|
}
|
||||||
|
ip_data_len -= hlen;
|
||||||
|
|
||||||
|
if (txdw0 & CP_TX_IPCS)
|
||||||
{
|
{
|
||||||
if (txdw0 & CP_TX_IPCS)
|
DPRINTF("+++ C+ mode need IP checksum\n");
|
||||||
{
|
|
||||||
DPRINTF("+++ C+ mode need IP checksum\n");
|
|
||||||
|
|
||||||
if (hlen<sizeof(ip_header) || hlen>eth_payload_len) {/* min header length */
|
ip->ip_sum = 0;
|
||||||
/* bad packet header len */
|
ip->ip_sum = ip_checksum(ip, hlen);
|
||||||
/* or packet too short */
|
DPRINTF("+++ C+ mode IP header len=%d checksum=%04x\n",
|
||||||
}
|
hlen, ip->ip_sum);
|
||||||
else
|
}
|
||||||
{
|
|
||||||
ip->ip_sum = 0;
|
if ((txdw0 & CP_TX_LGSEN) && ip_protocol == IP_PROTO_TCP)
|
||||||
ip->ip_sum = ip_checksum(ip, hlen);
|
{
|
||||||
DPRINTF("+++ C+ mode IP header len=%d checksum=%04x\n",
|
/* Large enough for the TCP header? */
|
||||||
hlen, ip->ip_sum);
|
if (ip_data_len < sizeof(tcp_header)) {
|
||||||
}
|
goto skip_offload;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((txdw0 & CP_TX_LGSEN) && ip_protocol == IP_PROTO_TCP)
|
int large_send_mss = (txdw0 >> 16) & CP_TC_LGSEN_MSS_MASK;
|
||||||
|
|
||||||
|
DPRINTF("+++ C+ mode offloaded task TSO MTU=%d IP data %d "
|
||||||
|
"frame data %d specified MSS=%d\n", ETH_MTU,
|
||||||
|
ip_data_len, saved_size - ETH_HLEN, large_send_mss);
|
||||||
|
|
||||||
|
int tcp_send_offset = 0;
|
||||||
|
int send_count = 0;
|
||||||
|
|
||||||
|
/* maximum IP header length is 60 bytes */
|
||||||
|
uint8_t saved_ip_header[60];
|
||||||
|
|
||||||
|
/* save IP header template; data area is used in tcp checksum calculation */
|
||||||
|
memcpy(saved_ip_header, eth_payload_data, hlen);
|
||||||
|
|
||||||
|
/* a placeholder for checksum calculation routine in tcp case */
|
||||||
|
uint8_t *data_to_checksum = eth_payload_data + hlen - 12;
|
||||||
|
// size_t data_to_checksum_len = eth_payload_len - hlen + 12;
|
||||||
|
|
||||||
|
/* pointer to TCP header */
|
||||||
|
tcp_header *p_tcp_hdr = (tcp_header*)(eth_payload_data + hlen);
|
||||||
|
|
||||||
|
int tcp_hlen = TCP_HEADER_DATA_OFFSET(p_tcp_hdr);
|
||||||
|
|
||||||
|
/* Invalid TCP data offset? */
|
||||||
|
if (tcp_hlen < sizeof(tcp_header) || tcp_hlen > ip_data_len) {
|
||||||
|
goto skip_offload;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ETH_MTU = ip header len + tcp header len + payload */
|
||||||
|
int tcp_data_len = ip_data_len - tcp_hlen;
|
||||||
|
int tcp_chunk_size = ETH_MTU - hlen - tcp_hlen;
|
||||||
|
|
||||||
|
DPRINTF("+++ C+ mode TSO IP data len %d TCP hlen %d TCP "
|
||||||
|
"data len %d TCP chunk size %d\n", ip_data_len,
|
||||||
|
tcp_hlen, tcp_data_len, tcp_chunk_size);
|
||||||
|
|
||||||
|
/* note the cycle below overwrites IP header data,
|
||||||
|
but restores it from saved_ip_header before sending packet */
|
||||||
|
|
||||||
|
int is_last_frame = 0;
|
||||||
|
|
||||||
|
for (tcp_send_offset = 0; tcp_send_offset < tcp_data_len; tcp_send_offset += tcp_chunk_size)
|
||||||
{
|
{
|
||||||
int large_send_mss = (txdw0 >> 16) & CP_TC_LGSEN_MSS_MASK;
|
uint16_t chunk_size = tcp_chunk_size;
|
||||||
|
|
||||||
DPRINTF("+++ C+ mode offloaded task TSO MTU=%d IP data %d "
|
/* check if this is the last frame */
|
||||||
"frame data %d specified MSS=%d\n", ETH_MTU,
|
if (tcp_send_offset + tcp_chunk_size >= tcp_data_len)
|
||||||
ip_data_len, saved_size - ETH_HLEN, large_send_mss);
|
|
||||||
|
|
||||||
int tcp_send_offset = 0;
|
|
||||||
int send_count = 0;
|
|
||||||
|
|
||||||
/* maximum IP header length is 60 bytes */
|
|
||||||
uint8_t saved_ip_header[60];
|
|
||||||
|
|
||||||
/* save IP header template; data area is used in tcp checksum calculation */
|
|
||||||
memcpy(saved_ip_header, eth_payload_data, hlen);
|
|
||||||
|
|
||||||
/* a placeholder for checksum calculation routine in tcp case */
|
|
||||||
uint8_t *data_to_checksum = eth_payload_data + hlen - 12;
|
|
||||||
// size_t data_to_checksum_len = eth_payload_len - hlen + 12;
|
|
||||||
|
|
||||||
/* pointer to TCP header */
|
|
||||||
tcp_header *p_tcp_hdr = (tcp_header*)(eth_payload_data + hlen);
|
|
||||||
|
|
||||||
int tcp_hlen = TCP_HEADER_DATA_OFFSET(p_tcp_hdr);
|
|
||||||
|
|
||||||
/* ETH_MTU = ip header len + tcp header len + payload */
|
|
||||||
int tcp_data_len = ip_data_len - tcp_hlen;
|
|
||||||
int tcp_chunk_size = ETH_MTU - hlen - tcp_hlen;
|
|
||||||
|
|
||||||
DPRINTF("+++ C+ mode TSO IP data len %d TCP hlen %d TCP "
|
|
||||||
"data len %d TCP chunk size %d\n", ip_data_len,
|
|
||||||
tcp_hlen, tcp_data_len, tcp_chunk_size);
|
|
||||||
|
|
||||||
/* note the cycle below overwrites IP header data,
|
|
||||||
but restores it from saved_ip_header before sending packet */
|
|
||||||
|
|
||||||
int is_last_frame = 0;
|
|
||||||
|
|
||||||
for (tcp_send_offset = 0; tcp_send_offset < tcp_data_len; tcp_send_offset += tcp_chunk_size)
|
|
||||||
{
|
{
|
||||||
uint16_t chunk_size = tcp_chunk_size;
|
is_last_frame = 1;
|
||||||
|
chunk_size = tcp_data_len - tcp_send_offset;
|
||||||
/* check if this is the last frame */
|
|
||||||
if (tcp_send_offset + tcp_chunk_size >= tcp_data_len)
|
|
||||||
{
|
|
||||||
is_last_frame = 1;
|
|
||||||
chunk_size = tcp_data_len - tcp_send_offset;
|
|
||||||
}
|
|
||||||
|
|
||||||
DPRINTF("+++ C+ mode TSO TCP seqno %08x\n",
|
|
||||||
be32_to_cpu(p_tcp_hdr->th_seq));
|
|
||||||
|
|
||||||
/* add 4 TCP pseudoheader fields */
|
|
||||||
/* copy IP source and destination fields */
|
|
||||||
memcpy(data_to_checksum, saved_ip_header + 12, 8);
|
|
||||||
|
|
||||||
DPRINTF("+++ C+ mode TSO calculating TCP checksum for "
|
|
||||||
"packet with %d bytes data\n", tcp_hlen +
|
|
||||||
chunk_size);
|
|
||||||
|
|
||||||
if (tcp_send_offset)
|
|
||||||
{
|
|
||||||
memcpy((uint8_t*)p_tcp_hdr + tcp_hlen, (uint8_t*)p_tcp_hdr + tcp_hlen + tcp_send_offset, chunk_size);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* keep PUSH and FIN flags only for the last frame */
|
|
||||||
if (!is_last_frame)
|
|
||||||
{
|
|
||||||
TCP_HEADER_CLEAR_FLAGS(p_tcp_hdr, TCP_FLAG_PUSH|TCP_FLAG_FIN);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* recalculate TCP checksum */
|
|
||||||
ip_pseudo_header *p_tcpip_hdr = (ip_pseudo_header *)data_to_checksum;
|
|
||||||
p_tcpip_hdr->zeros = 0;
|
|
||||||
p_tcpip_hdr->ip_proto = IP_PROTO_TCP;
|
|
||||||
p_tcpip_hdr->ip_payload = cpu_to_be16(tcp_hlen + chunk_size);
|
|
||||||
|
|
||||||
p_tcp_hdr->th_sum = 0;
|
|
||||||
|
|
||||||
int tcp_checksum = ip_checksum(data_to_checksum, tcp_hlen + chunk_size + 12);
|
|
||||||
DPRINTF("+++ C+ mode TSO TCP checksum %04x\n",
|
|
||||||
tcp_checksum);
|
|
||||||
|
|
||||||
p_tcp_hdr->th_sum = tcp_checksum;
|
|
||||||
|
|
||||||
/* restore IP header */
|
|
||||||
memcpy(eth_payload_data, saved_ip_header, hlen);
|
|
||||||
|
|
||||||
/* set IP data length and recalculate IP checksum */
|
|
||||||
ip->ip_len = cpu_to_be16(hlen + tcp_hlen + chunk_size);
|
|
||||||
|
|
||||||
/* increment IP id for subsequent frames */
|
|
||||||
ip->ip_id = cpu_to_be16(tcp_send_offset/tcp_chunk_size + be16_to_cpu(ip->ip_id));
|
|
||||||
|
|
||||||
ip->ip_sum = 0;
|
|
||||||
ip->ip_sum = ip_checksum(eth_payload_data, hlen);
|
|
||||||
DPRINTF("+++ C+ mode TSO IP header len=%d "
|
|
||||||
"checksum=%04x\n", hlen, ip->ip_sum);
|
|
||||||
|
|
||||||
int tso_send_size = ETH_HLEN + hlen + tcp_hlen + chunk_size;
|
|
||||||
DPRINTF("+++ C+ mode TSO transferring packet size "
|
|
||||||
"%d\n", tso_send_size);
|
|
||||||
rtl8139_transfer_frame(s, saved_buffer, tso_send_size,
|
|
||||||
0, (uint8_t *) dot1q_buffer);
|
|
||||||
|
|
||||||
/* add transferred count to TCP sequence number */
|
|
||||||
p_tcp_hdr->th_seq = cpu_to_be32(chunk_size + be32_to_cpu(p_tcp_hdr->th_seq));
|
|
||||||
++send_count;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Stop sending this frame */
|
DPRINTF("+++ C+ mode TSO TCP seqno %08x\n",
|
||||||
saved_size = 0;
|
be32_to_cpu(p_tcp_hdr->th_seq));
|
||||||
}
|
|
||||||
else if (txdw0 & (CP_TX_TCPCS|CP_TX_UDPCS))
|
|
||||||
{
|
|
||||||
DPRINTF("+++ C+ mode need TCP or UDP checksum\n");
|
|
||||||
|
|
||||||
/* maximum IP header length is 60 bytes */
|
|
||||||
uint8_t saved_ip_header[60];
|
|
||||||
memcpy(saved_ip_header, eth_payload_data, hlen);
|
|
||||||
|
|
||||||
uint8_t *data_to_checksum = eth_payload_data + hlen - 12;
|
|
||||||
// size_t data_to_checksum_len = eth_payload_len - hlen + 12;
|
|
||||||
|
|
||||||
/* add 4 TCP pseudoheader fields */
|
/* add 4 TCP pseudoheader fields */
|
||||||
/* copy IP source and destination fields */
|
/* copy IP source and destination fields */
|
||||||
memcpy(data_to_checksum, saved_ip_header + 12, 8);
|
memcpy(data_to_checksum, saved_ip_header + 12, 8);
|
||||||
|
|
||||||
if ((txdw0 & CP_TX_TCPCS) && ip_protocol == IP_PROTO_TCP)
|
DPRINTF("+++ C+ mode TSO calculating TCP checksum for "
|
||||||
|
"packet with %d bytes data\n", tcp_hlen +
|
||||||
|
chunk_size);
|
||||||
|
|
||||||
|
if (tcp_send_offset)
|
||||||
{
|
{
|
||||||
DPRINTF("+++ C+ mode calculating TCP checksum for "
|
memcpy((uint8_t*)p_tcp_hdr + tcp_hlen, (uint8_t*)p_tcp_hdr + tcp_hlen + tcp_send_offset, chunk_size);
|
||||||
"packet with %d bytes data\n", ip_data_len);
|
|
||||||
|
|
||||||
ip_pseudo_header *p_tcpip_hdr = (ip_pseudo_header *)data_to_checksum;
|
|
||||||
p_tcpip_hdr->zeros = 0;
|
|
||||||
p_tcpip_hdr->ip_proto = IP_PROTO_TCP;
|
|
||||||
p_tcpip_hdr->ip_payload = cpu_to_be16(ip_data_len);
|
|
||||||
|
|
||||||
tcp_header* p_tcp_hdr = (tcp_header *) (data_to_checksum+12);
|
|
||||||
|
|
||||||
p_tcp_hdr->th_sum = 0;
|
|
||||||
|
|
||||||
int tcp_checksum = ip_checksum(data_to_checksum, ip_data_len + 12);
|
|
||||||
DPRINTF("+++ C+ mode TCP checksum %04x\n",
|
|
||||||
tcp_checksum);
|
|
||||||
|
|
||||||
p_tcp_hdr->th_sum = tcp_checksum;
|
|
||||||
}
|
}
|
||||||
else if ((txdw0 & CP_TX_UDPCS) && ip_protocol == IP_PROTO_UDP)
|
|
||||||
|
/* keep PUSH and FIN flags only for the last frame */
|
||||||
|
if (!is_last_frame)
|
||||||
{
|
{
|
||||||
DPRINTF("+++ C+ mode calculating UDP checksum for "
|
TCP_HEADER_CLEAR_FLAGS(p_tcp_hdr, TCP_FLAG_PUSH|TCP_FLAG_FIN);
|
||||||
"packet with %d bytes data\n", ip_data_len);
|
|
||||||
|
|
||||||
ip_pseudo_header *p_udpip_hdr = (ip_pseudo_header *)data_to_checksum;
|
|
||||||
p_udpip_hdr->zeros = 0;
|
|
||||||
p_udpip_hdr->ip_proto = IP_PROTO_UDP;
|
|
||||||
p_udpip_hdr->ip_payload = cpu_to_be16(ip_data_len);
|
|
||||||
|
|
||||||
udp_header *p_udp_hdr = (udp_header *) (data_to_checksum+12);
|
|
||||||
|
|
||||||
p_udp_hdr->uh_sum = 0;
|
|
||||||
|
|
||||||
int udp_checksum = ip_checksum(data_to_checksum, ip_data_len + 12);
|
|
||||||
DPRINTF("+++ C+ mode UDP checksum %04x\n",
|
|
||||||
udp_checksum);
|
|
||||||
|
|
||||||
p_udp_hdr->uh_sum = udp_checksum;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* recalculate TCP checksum */
|
||||||
|
ip_pseudo_header *p_tcpip_hdr = (ip_pseudo_header *)data_to_checksum;
|
||||||
|
p_tcpip_hdr->zeros = 0;
|
||||||
|
p_tcpip_hdr->ip_proto = IP_PROTO_TCP;
|
||||||
|
p_tcpip_hdr->ip_payload = cpu_to_be16(tcp_hlen + chunk_size);
|
||||||
|
|
||||||
|
p_tcp_hdr->th_sum = 0;
|
||||||
|
|
||||||
|
int tcp_checksum = ip_checksum(data_to_checksum, tcp_hlen + chunk_size + 12);
|
||||||
|
DPRINTF("+++ C+ mode TSO TCP checksum %04x\n",
|
||||||
|
tcp_checksum);
|
||||||
|
|
||||||
|
p_tcp_hdr->th_sum = tcp_checksum;
|
||||||
|
|
||||||
/* restore IP header */
|
/* restore IP header */
|
||||||
memcpy(eth_payload_data, saved_ip_header, hlen);
|
memcpy(eth_payload_data, saved_ip_header, hlen);
|
||||||
|
|
||||||
|
/* set IP data length and recalculate IP checksum */
|
||||||
|
ip->ip_len = cpu_to_be16(hlen + tcp_hlen + chunk_size);
|
||||||
|
|
||||||
|
/* increment IP id for subsequent frames */
|
||||||
|
ip->ip_id = cpu_to_be16(tcp_send_offset/tcp_chunk_size + be16_to_cpu(ip->ip_id));
|
||||||
|
|
||||||
|
ip->ip_sum = 0;
|
||||||
|
ip->ip_sum = ip_checksum(eth_payload_data, hlen);
|
||||||
|
DPRINTF("+++ C+ mode TSO IP header len=%d "
|
||||||
|
"checksum=%04x\n", hlen, ip->ip_sum);
|
||||||
|
|
||||||
|
int tso_send_size = ETH_HLEN + hlen + tcp_hlen + chunk_size;
|
||||||
|
DPRINTF("+++ C+ mode TSO transferring packet size "
|
||||||
|
"%d\n", tso_send_size);
|
||||||
|
rtl8139_transfer_frame(s, saved_buffer, tso_send_size,
|
||||||
|
0, (uint8_t *) dot1q_buffer);
|
||||||
|
|
||||||
|
/* add transferred count to TCP sequence number */
|
||||||
|
p_tcp_hdr->th_seq = cpu_to_be32(chunk_size + be32_to_cpu(p_tcp_hdr->th_seq));
|
||||||
|
++send_count;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Stop sending this frame */
|
||||||
|
saved_size = 0;
|
||||||
|
}
|
||||||
|
else if (txdw0 & (CP_TX_TCPCS|CP_TX_UDPCS))
|
||||||
|
{
|
||||||
|
DPRINTF("+++ C+ mode need TCP or UDP checksum\n");
|
||||||
|
|
||||||
|
/* maximum IP header length is 60 bytes */
|
||||||
|
uint8_t saved_ip_header[60];
|
||||||
|
memcpy(saved_ip_header, eth_payload_data, hlen);
|
||||||
|
|
||||||
|
uint8_t *data_to_checksum = eth_payload_data + hlen - 12;
|
||||||
|
// size_t data_to_checksum_len = eth_payload_len - hlen + 12;
|
||||||
|
|
||||||
|
/* add 4 TCP pseudoheader fields */
|
||||||
|
/* copy IP source and destination fields */
|
||||||
|
memcpy(data_to_checksum, saved_ip_header + 12, 8);
|
||||||
|
|
||||||
|
if ((txdw0 & CP_TX_TCPCS) && ip_protocol == IP_PROTO_TCP)
|
||||||
|
{
|
||||||
|
DPRINTF("+++ C+ mode calculating TCP checksum for "
|
||||||
|
"packet with %d bytes data\n", ip_data_len);
|
||||||
|
|
||||||
|
ip_pseudo_header *p_tcpip_hdr = (ip_pseudo_header *)data_to_checksum;
|
||||||
|
p_tcpip_hdr->zeros = 0;
|
||||||
|
p_tcpip_hdr->ip_proto = IP_PROTO_TCP;
|
||||||
|
p_tcpip_hdr->ip_payload = cpu_to_be16(ip_data_len);
|
||||||
|
|
||||||
|
tcp_header* p_tcp_hdr = (tcp_header *) (data_to_checksum+12);
|
||||||
|
|
||||||
|
p_tcp_hdr->th_sum = 0;
|
||||||
|
|
||||||
|
int tcp_checksum = ip_checksum(data_to_checksum, ip_data_len + 12);
|
||||||
|
DPRINTF("+++ C+ mode TCP checksum %04x\n",
|
||||||
|
tcp_checksum);
|
||||||
|
|
||||||
|
p_tcp_hdr->th_sum = tcp_checksum;
|
||||||
|
}
|
||||||
|
else if ((txdw0 & CP_TX_UDPCS) && ip_protocol == IP_PROTO_UDP)
|
||||||
|
{
|
||||||
|
DPRINTF("+++ C+ mode calculating UDP checksum for "
|
||||||
|
"packet with %d bytes data\n", ip_data_len);
|
||||||
|
|
||||||
|
ip_pseudo_header *p_udpip_hdr = (ip_pseudo_header *)data_to_checksum;
|
||||||
|
p_udpip_hdr->zeros = 0;
|
||||||
|
p_udpip_hdr->ip_proto = IP_PROTO_UDP;
|
||||||
|
p_udpip_hdr->ip_payload = cpu_to_be16(ip_data_len);
|
||||||
|
|
||||||
|
udp_header *p_udp_hdr = (udp_header *) (data_to_checksum+12);
|
||||||
|
|
||||||
|
p_udp_hdr->uh_sum = 0;
|
||||||
|
|
||||||
|
int udp_checksum = ip_checksum(data_to_checksum, ip_data_len + 12);
|
||||||
|
DPRINTF("+++ C+ mode UDP checksum %04x\n",
|
||||||
|
udp_checksum);
|
||||||
|
|
||||||
|
p_udp_hdr->uh_sum = udp_checksum;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* restore IP header */
|
||||||
|
memcpy(eth_payload_data, saved_ip_header, hlen);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
skip_offload:
|
||||||
/* update tally counter */
|
/* update tally counter */
|
||||||
++s->tally_counters.TxOk;
|
++s->tally_counters.TxOk;
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue