/* * Adjust the checksum value of an ICMP/ICMPv6 packet, based on the * original type/code values and new type/code values. */ int cksum_update_icmp_type_code(void *icmp46_hdrp, int orig_type, int orig_code, int new_type, int new_code) { assert(icmp46_hdrp != NULL); struct icmp6_hdr *icmp6_hdrp = icmp46_hdrp; int32_t sum = icmp6_hdrp->icmp6_cksum; sum = ~sum & 0xffff; uint8_t typecode[2]; /* Subtract the original type/code values from the checksum value. */ typecode[0] = orig_type; typecode[1] = orig_code; sum -= cksum_acc_words((const uint16_t *)typecode, 2); /* Add the new type/code values to the checksum value. */ typecode[0] = new_type; typecode[1] = new_code; sum += cksum_acc_words((const uint16_t *)typecode, 2); ADDCARRY(sum); icmp6_hdrp->icmp6_cksum = ~sum & 0xffff; return (0); }
u_short in_addword(u_short a, u_short b) { u_int64_t sum = a + b; ADDCARRY(sum); return (sum); }
/* * Calculate the transport layer checksum value. The parameter must * contain the entire packet to calculate the sum. * * The ulp parameter contains the transport protocol number. The iov * parameter contains the following information. * * iov[0]: Address family (uint32_t), or struct tun_pi{} * iov[1]: IPv4/IPv6 header * iov[2]: IPv6 Fragment header (if necessary, otherwise NULL) * iov[3]: Upper layer protocol header * iov[4]: Upper layer protocol data */ int cksum_calc_ulp(int ulp, struct iovec *iov) { assert(iov != NULL); assert(iov[0].iov_base != NULL); assert(iov[1].iov_base != NULL); assert(iov[3].iov_base != NULL); int32_t sum; struct icmp *icmp4_hdrp; struct icmp6_hdr *icmp6_hdrp; switch (ulp) { case IPPROTO_ICMP: icmp4_hdrp = iov[3].iov_base; icmp4_hdrp->icmp_cksum = 0; sum = cksum_acc_words(iov[3].iov_base, iov[3].iov_len); if (iov[4].iov_base != NULL) { sum += cksum_acc_words(iov[4].iov_base, iov[4].iov_len); } ADDCARRY(sum); icmp4_hdrp->icmp_cksum = ~sum & 0xffff; break; case IPPROTO_ICMPV6: icmp6_hdrp = iov[3].iov_base; icmp6_hdrp->icmp6_cksum = 0; sum = cksum_acc_ip_pheader(iov[1].iov_base); sum += cksum_acc_words(iov[3].iov_base, iov[3].iov_len); if (iov[4].iov_base != NULL) { sum += cksum_acc_words(iov[4].iov_base, iov[4].iov_len); } ADDCARRY(sum); icmp6_hdrp->icmp6_cksum = ~sum & 0xfff; break; default: warnx("unsupported upper layer protocol %d.", ulp); return (-1); } return (0); }
/* Calculate the checksum value of an IPv4 header. */ uint16_t cksum_calc_ip4_header(const struct ip *ip4_hdrp) { assert(ip4_hdrp != NULL); int32_t sum = 0; int ip4_header_len = ip4_hdrp->ip_hl << 2; sum = cksum_acc_words((uint16_t *)ip4_hdrp, ip4_header_len); ADDCARRY(sum); return (~sum & 0xffff); }
/* * Adjust the transport layer checksum value based on the difference * between the incoming IP header and the outgoing IP header values, * and update the checksum field appropriately. * * The ulp parameter contains the transport protocol number. The * orig_ip_hdrp parameter points the incoming IP header. The iov * parameter contains the following information. * * iov[0]: Address family (uint32_t), or struct tun_pi{} * iov[1]: IPv4/IPv6 header * iov[2]: IPv6 Fragment header (if necessary, otherwise NULL) * iov[3]: Upper layer protocol data (at least, a header must exist) */ int cksum_update_ulp(int ulp, const void *orig_ip_hdrp, struct iovec *iov) { assert(orig_ip_hdrp != NULL); assert(iov != NULL); assert(iov[0].iov_base != NULL); assert(iov[1].iov_base != NULL); assert(iov[3].iov_base != NULL); struct tcphdr *tcp_hdrp; struct udphdr *udp_hdrp; struct icmp *icmp_hdrp; struct icmp6_hdr *icmp6_hdrp; int32_t sum; switch (ulp) { case IPPROTO_ICMP: icmp_hdrp = iov[3].iov_base; sum = icmp_hdrp->icmp_cksum; sum = ~sum & 0xffff; sum -= cksum_acc_ip_pheader(orig_ip_hdrp); /* * ICMPv6 includes the sum of the pseudo IPv6 header in its * checksum calculation, but ICMP doesn't. We just subtract the * original pseudo IPv6 header sum from the checksum value. */ ADDCARRY(sum); icmp_hdrp->icmp_cksum = ~sum & 0xffff; break; case IPPROTO_ICMPV6: icmp6_hdrp = iov[3].iov_base; sum = icmp6_hdrp->icmp6_cksum; sum = ~sum & 0xffff; sum += cksum_acc_ip_pheader(iov[1].iov_base); /* * Similar to the ICMP case above, we just add the new pseudo IPv6 * header sum to the checksum value, since the original ICMP * checksum doesn't include the IP pseudo header sum. */ ADDCARRY(sum); icmp6_hdrp->icmp6_cksum = ~sum & 0xffff; break; #if defined(__linux__) #define th_sum check #define uh_sum check #endif case IPPROTO_TCP: tcp_hdrp = iov[3].iov_base; sum = tcp_hdrp->th_sum; sum = ~sum & 0xffff; sum -= cksum_acc_ip_pheader_wo_payload_len(orig_ip_hdrp); sum += cksum_acc_ip_pheader_wo_payload_len(iov[1].iov_base); ADDCARRY(sum); tcp_hdrp->th_sum = ~sum & 0xffff; break; case IPPROTO_UDP: udp_hdrp = iov[3].iov_base; sum = udp_hdrp->uh_sum; sum = ~sum & 0xffff; sum -= cksum_acc_ip_pheader_wo_payload_len(orig_ip_hdrp); sum += cksum_acc_ip_pheader_wo_payload_len(iov[1].iov_base); ADDCARRY(sum); udp_hdrp->uh_sum = ~sum & 0xffff; break; #if defined(__linux__) #undef th_sum #undef uh_sum #endif default: warnx("unsupported upper layer protocol %d.", ulp); return (-1); } return (0); }