diff options
Diffstat (limited to 'test/packetdrill/packet_checksum.c')
-rw-r--r-- | test/packetdrill/packet_checksum.c | 116 |
1 files changed, 116 insertions, 0 deletions
diff --git a/test/packetdrill/packet_checksum.c b/test/packetdrill/packet_checksum.c new file mode 100644 index 0000000..d5164b3 --- /dev/null +++ b/test/packetdrill/packet_checksum.c @@ -0,0 +1,116 @@ +/* + * Copyright 2013 Google Inc. + * + * 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; either version 2 + * of the License, or (at your option) any later version. + * + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ +/* + * Author: ncardwell@google.com (Neal Cardwell) + * + * Implementation for a module to checksum TCP/IP packets. + */ + +#include "packet_checksum.h" + +#include "checksum.h" +#include "icmp.h" +#include "icmpv6.h" +#include "ip.h" +#include "ipv6.h" +#include "tcp.h" + +static void checksum_ipv4_packet(struct packet *packet) +{ + struct ipv4 *ipv4 = packet->ipv4; + + /* Fill in IPv4 header checksum. */ + ipv4->check = 0; + ipv4->check = ipv4_checksum(ipv4, ipv4_header_len(ipv4)); + assert(packet->ip_bytes >= ntohs(ipv4->tot_len)); + + /* Find the length of layer 4 header, options, and payload. */ + const int l4_bytes = ntohs(ipv4->tot_len) - ipv4_header_len(ipv4); + assert(l4_bytes > 0); + + /* Fill in IPv4-based layer 4 checksum. */ + if (packet->tcp != NULL) { + struct tcp *tcp = packet->tcp; + tcp->check = 0; + tcp->check = tcp_udp_v4_checksum(ipv4->src_ip, + ipv4->dst_ip, + IPPROTO_TCP, tcp, l4_bytes); + } else if (packet->udp != NULL) { + struct udp *udp = packet->udp; + udp->check = 0; + udp->check = tcp_udp_v4_checksum(ipv4->src_ip, + ipv4->dst_ip, + IPPROTO_UDP, udp, l4_bytes); + } else if (packet->icmpv4 != NULL) { + struct icmpv4 *icmpv4 = packet->icmpv4; + icmpv4->checksum = 0; + icmpv4->checksum = ipv4_checksum(icmpv4, l4_bytes); + } else { + assert(!"not TCP or ICMP"); + } +} + +static void checksum_ipv6_packet(struct packet *packet) +{ + struct ipv6 *ipv6 = packet->ipv6; + + /* IPv6 has no header checksum. */ + /* For now we do not support IPv6 extension headers. */ + assert(packet->ip_bytes >= sizeof(*ipv6) + ntohs(ipv6->payload_len)); + + /* Find the length of layer 4 header, options, and payload. */ + const int l4_bytes = ntohs(ipv6->payload_len); + assert(l4_bytes > 0); + + /* Fill in IPv6-based layer 4 checksum. */ + if (packet->tcp != NULL) { + struct tcp *tcp = packet->tcp; + tcp->check = 0; + tcp->check = tcp_udp_v6_checksum(&ipv6->src_ip, + &ipv6->dst_ip, + IPPROTO_TCP, tcp, l4_bytes); + } else if (packet->udp != NULL) { + struct udp *udp = packet->udp; + udp->check = 0; + udp->check = tcp_udp_v6_checksum(&ipv6->src_ip, + &ipv6->dst_ip, + IPPROTO_UDP, udp, l4_bytes); + } else if (packet->icmpv6 != NULL) { + /* IPv6 ICMP has a pseudo-header checksum, like TCP. */ + struct icmpv6 *icmpv6 = packet->icmpv6; + icmpv6->checksum = 0; + icmpv6->checksum = + tcp_udp_v6_checksum(&ipv6->src_ip, + &ipv6->dst_ip, + IPPROTO_ICMPV6, icmpv6, l4_bytes); + } else { + assert(!"not TCP or ICMP"); + } +} + +void checksum_packet(struct packet *packet) +{ + int address_family = packet_address_family(packet); + if (address_family == AF_INET) + return checksum_ipv4_packet(packet); + else if (address_family == AF_INET6) + return checksum_ipv6_packet(packet); + else + assert(!"bad ip version"); +} |