aboutsummaryrefslogtreecommitdiffstats
path: root/test/packetdrill/packet_checksum.c
diff options
context:
space:
mode:
Diffstat (limited to 'test/packetdrill/packet_checksum.c')
-rw-r--r--test/packetdrill/packet_checksum.c116
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");
+}