| /* |
| * IP checksumming functions. |
| * (c) 2008 Gerd Hoffmann <kraxel@redhat.com> |
| * |
| * This program is free software; you can redistribute it and/or modify |
| * it under the terms of the GNU General Public License as published by |
| * the Free Software Foundation; under version 2 of the License. |
| * |
| * This program is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| * GNU General Public License for more details. |
| * |
| * You should have received a copy of the GNU General Public License |
| * along with this program; if not, see <http://www.gnu.org/licenses/>. |
| */ |
| |
| #include "hw/hw.h" |
| #include "net/net.h" |
| |
| #define PROTO_TCP 6 |
| #define PROTO_UDP 17 |
| |
| uint32_t net_checksum_add(int len, uint8_t *buf) |
| { |
| uint32_t sum = 0; |
| int i; |
| |
| for (i = 0; i < len; i++) { |
| if (i & 1) |
| sum += (uint32_t)buf[i]; |
| else |
| sum += (uint32_t)buf[i] << 8; |
| } |
| return sum; |
| } |
| |
| uint16_t net_checksum_finish(uint32_t sum) |
| { |
| while (sum>>16) |
| sum = (sum & 0xFFFF)+(sum >> 16); |
| return ~sum; |
| } |
| |
| uint16_t net_checksum_tcpudp(uint16_t length, uint16_t proto, |
| uint8_t *addrs, uint8_t *buf) |
| { |
| uint32_t sum = 0; |
| |
| sum += net_checksum_add(length, buf); // payload |
| sum += net_checksum_add(8, addrs); // src + dst address |
| sum += proto + length; // protocol & length |
| return net_checksum_finish(sum); |
| } |
| |
| void net_checksum_calculate(uint8_t *data, int length) |
| { |
| int hlen, plen, proto, csum_offset; |
| uint16_t csum; |
| |
| if ((data[14] & 0xf0) != 0x40) |
| return; /* not IPv4 */ |
| hlen = (data[14] & 0x0f) * 4; |
| plen = (data[16] << 8 | data[17]) - hlen; |
| proto = data[23]; |
| |
| switch (proto) { |
| case PROTO_TCP: |
| csum_offset = 16; |
| break; |
| case PROTO_UDP: |
| csum_offset = 6; |
| break; |
| default: |
| return; |
| } |
| |
| if (plen < csum_offset+2) |
| return; |
| |
| data[14+hlen+csum_offset] = 0; |
| data[14+hlen+csum_offset+1] = 0; |
| csum = net_checksum_tcpudp(plen, proto, data+14+12, data+14+hlen); |
| data[14+hlen+csum_offset] = csum >> 8; |
| data[14+hlen+csum_offset+1] = csum & 0xff; |
| } |