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_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->sctp != NULL) { struct sctp_common_header *sctp = packet->sctp; sctp->crc32c = 0; sctp->crc32c = sctp_crc32c(sctp, l4_bytes); } else 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->udplite != NULL) { struct udplite *udplite = packet->udplite; u16 coverage; coverage = ntohs(udplite->cov); if ((coverage == 0) || (coverage > l4_bytes)) coverage = l4_bytes; udplite->check = 0; udplite->check = udplite_v4_checksum(ipv4->src_ip, ipv4->dst_ip, IPPROTO_UDPLITE, udplite, l4_bytes, coverage); } else if (packet->icmpv4 != NULL) { struct icmpv4 *icmpv4 = packet->icmpv4; icmpv4->checksum = 0; icmpv4->checksum = ipv4_checksum(icmpv4, l4_bytes); } else { assert(!"not SCTP or TCP or UDP or UDPLite or ICMP"); } }
/* Verify IP and TCP checksums on an outbound live packet. */ static int verify_outbound_live_checksums(struct packet *live_packet, char **error) { /* Verify IP header checksum. */ if ((live_packet->ipv4 != NULL) && ipv4_checksum(live_packet->ipv4, ipv4_header_len(live_packet->ipv4))) { asprintf(error, "bad outbound IP checksum"); return STATUS_ERR; } /* TODO(ncardwell): Verify TCP and UDP checksum. This is a little * subtle, due to TCP checksum offloading. */ return STATUS_OK; }
/* Parse the IPv4 header and the TCP header inside. Return a * packet_parse_result_t. * Note that packet_end points to the byte beyond the end of packet. */ static int parse_ipv4(struct packet *packet, u8 *header_start, u8 *packet_end, char **error) { struct header *ip_header = NULL; u8 *p = header_start; const bool is_outer = (packet->ip_bytes == 0); bool is_inner = false; enum packet_parse_result_t result = PACKET_BAD; struct ipv4 *ipv4 = (struct ipv4 *) (p); const int ip_header_bytes = ipv4_header_len(ipv4); assert(ip_header_bytes >= 0); if (ip_header_bytes < sizeof(*ipv4)) { asprintf(error, "IP header too short"); goto error_out; } if (p + ip_header_bytes > packet_end) { asprintf(error, "Full IP header overflows packet"); goto error_out; } const int ip_total_bytes = ntohs(ipv4->tot_len); if (p + ip_total_bytes > packet_end) { asprintf(error, "IP payload overflows packet"); goto error_out; } if (ip_header_bytes > ip_total_bytes) { asprintf(error, "IP header bigger than datagram"); goto error_out; } if (ntohs(ipv4->frag_off) & IP_MF) { /* more fragments? */ asprintf(error, "More fragments remaining"); goto error_out; } if (ntohs(ipv4->frag_off) & IP_OFFMASK) { /* fragment offset */ asprintf(error, "Non-zero fragment offset"); goto error_out; } const u16 checksum = ipv4_checksum(ipv4, ip_header_bytes); if (checksum != 0) { asprintf(error, "Bad IP checksum"); goto error_out; } ip_header = packet_append_header(packet, HEADER_IPV4, ip_header_bytes); if (ip_header == NULL) { asprintf(error, "Too many nested headers at IPv4 header"); goto error_out; } ip_header->total_bytes = ip_total_bytes; /* Move on to the header inside. */ p += ip_header_bytes; assert(p <= packet_end); if (DEBUG_LOGGING) { char src_string[ADDR_STR_LEN]; char dst_string[ADDR_STR_LEN]; struct ip_address src_ip, dst_ip; ip_from_ipv4(&ipv4->src_ip, &src_ip); ip_from_ipv4(&ipv4->dst_ip, &dst_ip); DEBUGP("src IP: %s\n", ip_to_string(&src_ip, src_string)); DEBUGP("dst IP: %s\n", ip_to_string(&dst_ip, dst_string)); } /* Examine the L4 header. */ const int layer4_bytes = ip_total_bytes - ip_header_bytes; const int layer4_protocol = ipv4->protocol; result = parse_layer4(packet, p, layer4_protocol, layer4_bytes, packet_end, &is_inner, error); /* If this is the innermost IP header then this is the primary. */ if (is_inner) packet->ipv4 = ipv4; /* If this is the outermost IP header then this is the packet length. */ if (is_outer) packet->ip_bytes = ip_total_bytes; return result; error_out: return PACKET_BAD; }