--- embedaddon/libnet/src/libnet_checksum.c 2012/02/21 22:14:23 1.1.1.1 +++ embedaddon/libnet/src/libnet_checksum.c 2023/09/27 11:11:38 1.1.1.3 @@ -1,5 +1,5 @@ /* - * $Id: libnet_checksum.c,v 1.1.1.1 2012/02/21 22:14:23 misho Exp $ + * $Id: libnet_checksum.c,v 1.1.1.3 2023/09/27 11:11:38 misho Exp $ * * libnet * libnet_checksum.c - checksum routines @@ -30,18 +30,18 @@ * */ -#if (HAVE_CONFIG_H) -#include "../include/config.h" -#endif -#if (!(_WIN32) || (__CYGWIN__)) -#include "../include/libnet.h" -#else -#include "../include/win32/libnet.h" -#endif +#include "common.h" + +/* Note: len is in bytes, not 16-bit words! */ int -libnet_in_cksum(u_int16_t *addr, int len) +libnet_in_cksum(uint16_t *addr, int len) { - int sum; + int sum = 0; + union + { + uint16_t s; + uint8_t b[2]; + } pad; sum = 0; @@ -50,9 +50,12 @@ libnet_in_cksum(u_int16_t *addr, int len) sum += *addr++; len -= 2; } + if (len == 1) { - sum += *(u_int16_t *)addr; + pad.b[0] = *(uint8_t *)addr; + pad.b[1] = 0; + sum += pad.s; } return (sum); @@ -95,61 +98,160 @@ libnet_toggle_checksum(libnet_t *l, libnet_ptag_t ptag } } +static int check_ip_payload_size(libnet_t*l, const uint8_t *iphdr, int ip_hl, int h_len, const uint8_t * end, const char* func) +{ + if((iphdr+ip_hl+h_len) > end) + { + snprintf(l->err_buf, LIBNET_ERRBUF_SIZE, + "%s(): ip payload not inside packet (pktsz %d, iphsz %d, payloadsz %d)", func, + (int)(end - iphdr), ip_hl, h_len); + return -1; + } + return 0; +} + + +/* + * For backwards binary compatibility. The calculations done here can easily + * result in buffer overreads and overwrites. You have been warned. And no, it + * is not possible to fix, the API contains no information on the buffer's + * boundary. libnet itself calls the safe function, libnet_inet_checksum(). So + * should you. + */ int -libnet_do_checksum(libnet_t *l, u_int8_t *buf, int protocol, int len) +libnet_do_checksum(libnet_t *l, uint8_t *iphdr, int protocol, int h_len) { - /* will need to update this for ipv6 at some point */ - struct libnet_ipv4_hdr *iph_p; - struct libnet_ipv6_hdr *ip6h_p; - int is_ipv6; - int ip_hl; - int sum; + uint16_t ip_len = 0; + struct libnet_ipv4_hdr* ip4 = (struct libnet_ipv4_hdr *)iphdr; + struct libnet_ipv6_hdr* ip6 = (struct libnet_ipv6_hdr *)iphdr; - is_ipv6 = 0; /* default to not using IPv6 */ - sum = 0; - iph_p = NULL; - ip6h_p = NULL; + if(ip4->ip_v == 6) { + ip_len = ntohs(ip6->ip_len); + } else { + ip_len = ntohs(ip4->ip_len); + } - if (len == 0) + return libnet_inet_checksum(l, iphdr, protocol, h_len, + iphdr, iphdr + ip_len + ); +} + + +#define CHECK_IP_PAYLOAD_SIZE() do { \ + int e=check_ip_payload_size(l,iphdr,ip_hl, h_len, end, __func__);\ + if(e) return e;\ +} while(0) + + +/* + * We are checksumming pblock "q" + * + * iphdr is the pointer to it's encapsulating IP header + * protocol describes the type of "q", expressed as an IPPROTO_ value + * h_len is the h_len from "q" + */ +int +libnet_inet_checksum(libnet_t *l, uint8_t *iphdr, int protocol, int h_len, const uint8_t *beg, const uint8_t * end) +{ + /* will need to update this for ipv6 at some point */ + struct libnet_ipv4_hdr *iph_p = (struct libnet_ipv4_hdr *)iphdr; + struct libnet_ipv6_hdr *ip6h_p = NULL; /* default to not using IPv6 */ + int ip_hl = 0; + int sum = 0; + uint8_t ip_nh = 0; + + /* Check for memory under/over reads/writes. */ + if(iphdr < beg || (iphdr+sizeof(*iph_p)) > end) { snprintf(l->err_buf, LIBNET_ERRBUF_SIZE, - "%s(): header length can't be zero\n", __func__); - return (-1); + "%s(): ipv4 hdr not inside packet (where %d, size %d)", __func__, + (int)(iphdr-beg), (int)(end-beg)); + return -1; } /* * Figure out which IP version we're dealing with. We'll assume v4 * and overlay a header structure to yank out the version. */ - iph_p = (struct libnet_ipv4_hdr *)buf; - if (iph_p && iph_p->ip_v == 6) + if (iph_p->ip_v == 6) { - ip6h_p = (struct libnet_ipv6_hdr *)buf; - is_ipv6 = 1; + ip6h_p = (struct libnet_ipv6_hdr *)iph_p; + iph_p = NULL; ip_hl = 40; + ip_nh = ip6h_p->ip_nh; + + if((uint8_t*)(ip6h_p+1) > end) + { + snprintf(l->err_buf, LIBNET_ERRBUF_SIZE, + "%s(): ipv6 hdr not inside packet", __func__); + return -1; + } + + /* FIXME this entire fragile exercise would be avoided if we just passed + * in the pointer to the protocol block 'q' we are checksumming, which + * we know. + */ + while (ip_nh != protocol && (uint8_t*)ip6h_p + ip_hl + 1 < end) + { + /* next header is not the upper layer protocol */ + switch (ip_nh) + { + case IPPROTO_DSTOPTS: + case IPPROTO_HOPOPTS: + case IPPROTO_ROUTING: + case IPPROTO_FRAGMENT: + case IPPROTO_AH: + case IPPROTO_ESP: + case IPPROTO_MH: + /* + * count option headers to the header length for + * checksum processing + */ + /* Common structure of ipv6 ext headers is: + * uint8: next header protocol + * uint8: length of this header, in multiples of 8, not + * including first eight octets + * The pointer arithmetic below follows from above. + */ + ip_nh = *((uint8_t*)ip6h_p+ip_hl); /* next next header */ + ip_hl += (*((uint8_t*)ip6h_p+ip_hl+1)+1)*8; /* ext header length */ + break; + default: + snprintf(l->err_buf, LIBNET_ERRBUF_SIZE, + "%s(): unsupported extension header (%d)", __func__, ip_nh); + return -1; + } + + } } else { - is_ipv6 = 0; ip_hl = iph_p->ip_hl << 2; } + if((iphdr+ip_hl) > end) + { + snprintf(l->err_buf, LIBNET_ERRBUF_SIZE, + "%s(): ip hdr len not inside packet", __func__); + return -1; + } + /* * Dug Song came up with this very cool checksuming implementation * eliminating the need for explicit psuedoheader use. Check it out. */ switch (protocol) { - /* - * Style note: normally I don't advocate declaring variables inside - * blocks of control, but it makes good sense here. -- MDS - */ case IPPROTO_TCP: { struct libnet_tcp_hdr *tcph_p = - (struct libnet_tcp_hdr *)(buf + ip_hl); + (struct libnet_tcp_hdr *)(iphdr + ip_hl); + h_len = (int)(end - (uint8_t*) tcph_p); /* ignore h_len, sum the packet we've coalesced */ + + CHECK_IP_PAYLOAD_SIZE(); + #if (STUPID_SOLARIS_CHECKSUM_BUG) tcph_p->th_sum = tcph_p->th_off << 2; return (1); @@ -162,64 +264,114 @@ libnet_do_checksum(libnet_t *l, u_int8_t *buf, int pro * the size of the TCP payload (only for raw sockets). */ tcph_p->th_sum = (tcph_p->th_off << 2) + - (len - (tcph_p->th_off << 2)); + (h_len - (tcph_p->th_off << 2)); return (1); } #endif + /* TCP checksum is over the IP pseudo header: + * ip src + * ip dst + * tcp protocol (IPPROTO_TCP) + * tcp length, including the header + * + the TCP header (with checksum set to zero) and data + */ tcph_p->th_sum = 0; - if (is_ipv6) + if (ip6h_p) { - sum = libnet_in_cksum((u_int16_t *)&ip6h_p->ip_src, 32); + sum = libnet_in_cksum((uint16_t *)&ip6h_p->ip_src, 32); } else { - sum = libnet_in_cksum((u_int16_t *)&iph_p->ip_src, 8); + /* 8 = src and dst */ + sum = libnet_in_cksum((uint16_t *)&iph_p->ip_src, 8); } - sum += ntohs(IPPROTO_TCP + len); - sum += libnet_in_cksum((u_int16_t *)tcph_p, len); + sum += ntohs(IPPROTO_TCP + h_len); + sum += libnet_in_cksum((uint16_t *)tcph_p, h_len); tcph_p->th_sum = LIBNET_CKSUM_CARRY(sum); +#if 0 + printf("tcp sum calculated: %#x/%d h_len %d\n", + ntohs(tcph_p->th_sum), + ntohs(tcph_p->th_sum), + h_len + ); +#endif break; } case IPPROTO_UDP: { struct libnet_udp_hdr *udph_p = - (struct libnet_udp_hdr *)(buf + ip_hl); + (struct libnet_udp_hdr *)(iphdr + ip_hl); + + h_len = (int)(end - (uint8_t*) udph_p); /* ignore h_len, sum the packet we've coalesced */ + + CHECK_IP_PAYLOAD_SIZE(); + udph_p->uh_sum = 0; - if (is_ipv6) + if (ip6h_p) { - sum = libnet_in_cksum((u_int16_t *)&ip6h_p->ip_src, 32); + sum = libnet_in_cksum((uint16_t *)&ip6h_p->ip_src, 32); } else { - sum = libnet_in_cksum((u_int16_t *)&iph_p->ip_src, 8); + sum = libnet_in_cksum((uint16_t *)&iph_p->ip_src, 8); } - sum += ntohs(IPPROTO_UDP + len); - sum += libnet_in_cksum((u_int16_t *)udph_p, len); + sum += ntohs(IPPROTO_UDP + h_len); + sum += libnet_in_cksum((uint16_t *)udph_p, h_len); udph_p->uh_sum = LIBNET_CKSUM_CARRY(sum); break; } case IPPROTO_ICMP: { struct libnet_icmpv4_hdr *icmph_p = - (struct libnet_icmpv4_hdr *)(buf + ip_hl); + (struct libnet_icmpv4_hdr *)(iphdr + ip_hl); + h_len = (int)(end - (uint8_t*) icmph_p); /* ignore h_len, sum the packet we've coalesced */ + + CHECK_IP_PAYLOAD_SIZE(); + icmph_p->icmp_sum = 0; - if (is_ipv6) + /* Hm, is this valid? Is the checksum algorithm for ICMPv6 encapsulated in IPv4 + * actually defined? + */ + if (ip6h_p) { - sum = libnet_in_cksum((u_int16_t *)&ip6h_p->ip_src, 32); - sum += ntohs(IPPROTO_ICMP6 + len); + sum = libnet_in_cksum((uint16_t *)&ip6h_p->ip_src, 32); + sum += ntohs(IPPROTO_ICMP6 + h_len); } - sum += libnet_in_cksum((u_int16_t *)icmph_p, len); + sum += libnet_in_cksum((uint16_t *)icmph_p, h_len); icmph_p->icmp_sum = LIBNET_CKSUM_CARRY(sum); break; } + case IPPROTO_ICMPV6: + { + struct libnet_icmpv6_hdr *icmph_p = + (struct libnet_icmpv6_hdr *)(iphdr + ip_hl); + + h_len = (int)(end - (uint8_t*) icmph_p); /* ignore h_len, sum the packet we've coalesced */ + + CHECK_IP_PAYLOAD_SIZE(); + + icmph_p->icmp_sum = 0; + if (ip6h_p) + { + sum = libnet_in_cksum((uint16_t *)&ip6h_p->ip_src, 32); + sum += ntohs(IPPROTO_ICMP6 + h_len); + } + sum += libnet_in_cksum((uint16_t *)icmph_p, h_len); + icmph_p->icmp_sum = LIBNET_CKSUM_CARRY(sum); + break; + } case IPPROTO_IGMP: { struct libnet_igmp_hdr *igmph_p = - (struct libnet_igmp_hdr *)(buf + ip_hl); + (struct libnet_igmp_hdr *)(iphdr + ip_hl); + h_len = (int)(end - (uint8_t*) igmph_p); /* ignore h_len, sum the packet we've coalesced */ + + CHECK_IP_PAYLOAD_SIZE(); + igmph_p->igmp_sum = 0; - sum = libnet_in_cksum((u_int16_t *)igmph_p, len); + sum = libnet_in_cksum((uint16_t *)igmph_p, h_len); igmph_p->igmp_sum = LIBNET_CKSUM_CARRY(sum); break; } @@ -229,39 +381,45 @@ libnet_do_checksum(libnet_t *l, u_int8_t *buf, int pro * in the multiple RFC version of the protocol ... ouf !!! */ struct libnet_gre_hdr *greh_p = - (struct libnet_gre_hdr *)(buf + ip_hl); - u_int16_t fv = ntohs(greh_p->flags_ver); + (struct libnet_gre_hdr *)(iphdr + ip_hl); + uint16_t fv = ntohs(greh_p->flags_ver); + + CHECK_IP_PAYLOAD_SIZE(); + if (!(fv & (GRE_CSUM|GRE_ROUTING | GRE_VERSION_0)) || !(fv & (GRE_CSUM|GRE_VERSION_1))) { snprintf(l->err_buf, LIBNET_ERRBUF_SIZE, - "%s(): can't compute GRE checksum (wrong flags_ver bits: 0x%x )\n", __func__, fv); + "%s(): can't compute GRE checksum (wrong flags_ver bits: 0x%x )", __func__, fv); return (-1); } - sum = libnet_in_cksum((u_int16_t *)greh_p, len); + sum = libnet_in_cksum((uint16_t *)greh_p, h_len); greh_p->gre_sum = LIBNET_CKSUM_CARRY(sum); break; } case IPPROTO_OSPF: { struct libnet_ospf_hdr *oh_p = - (struct libnet_ospf_hdr *)(buf + ip_hl); + (struct libnet_ospf_hdr *)(iphdr + ip_hl); + CHECK_IP_PAYLOAD_SIZE(); + oh_p->ospf_sum = 0; - sum += libnet_in_cksum((u_int16_t *)oh_p, len); + sum += libnet_in_cksum((uint16_t *)oh_p, h_len); oh_p->ospf_sum = LIBNET_CKSUM_CARRY(sum); break; } case IPPROTO_OSPF_LSA: { struct libnet_ospf_hdr *oh_p = - (struct libnet_ospf_hdr *)(buf + ip_hl); + (struct libnet_ospf_hdr *)(iphdr + ip_hl); struct libnet_lsa_hdr *lsa_p = - (struct libnet_lsa_hdr *)(buf + + (struct libnet_lsa_hdr *)(iphdr + ip_hl + oh_p->ospf_len); + /* FIXME need additional length check, to account for ospf_len */ lsa_p->lsa_sum = 0; - sum += libnet_in_cksum((u_int16_t *)lsa_p, len); + sum += libnet_in_cksum((uint16_t *)lsa_p, h_len); lsa_p->lsa_sum = LIBNET_CKSUM_CARRY(sum); break; #if 0 @@ -270,7 +428,7 @@ libnet_do_checksum(libnet_t *l, u_int8_t *buf, int pro */ int c0, c1; struct libnet_lsa_hdr *lsa_p = (struct libnet_lsa_hdr *)buf; - u_int8_t *p, *p1, *p2, *p3; + uint8_t *p, *p1, *p2, *p3; c0 = 0; c1 = 0; @@ -319,35 +477,50 @@ libnet_do_checksum(libnet_t *l, u_int8_t *buf, int pro } case IPPROTO_IP: { - iph_p->ip_sum = 0; - sum = libnet_in_cksum((u_int16_t *)iph_p, ip_hl); - iph_p->ip_sum = LIBNET_CKSUM_CARRY(sum); + if(!iph_p) { + /* IPv6 doesn't have a checksum */ + } else { + iph_p->ip_sum = 0; + sum = libnet_in_cksum((uint16_t *)iph_p, ip_hl); + iph_p->ip_sum = LIBNET_CKSUM_CARRY(sum); + } break; } case IPPROTO_VRRP: { struct libnet_vrrp_hdr *vrrph_p = - (struct libnet_vrrp_hdr *)(buf + ip_hl); + (struct libnet_vrrp_hdr *)(iphdr + ip_hl); + CHECK_IP_PAYLOAD_SIZE(); vrrph_p->vrrp_sum = 0; - sum = libnet_in_cksum((u_int16_t *)vrrph_p, len); + sum = libnet_in_cksum((uint16_t *)vrrph_p, h_len); vrrph_p->vrrp_sum = LIBNET_CKSUM_CARRY(sum); break; } case LIBNET_PROTO_CDP: { /* XXX - Broken: how can we easily get the entire packet size? */ + /* FIXME you can't, checksumming non-IP protocols was not supported by libnet */ struct libnet_cdp_hdr *cdph_p = - (struct libnet_cdp_hdr *)buf; + (struct libnet_cdp_hdr *)iphdr; + if((iphdr+h_len) > end) + { + snprintf(l->err_buf, LIBNET_ERRBUF_SIZE, + "%s(): cdp payload not inside packet", __func__); + return -1; + } + cdph_p->cdp_sum = 0; - sum = libnet_in_cksum((u_int16_t *)cdph_p, len); + sum = libnet_in_cksum((uint16_t *)cdph_p, h_len); cdph_p->cdp_sum = LIBNET_CKSUM_CARRY(sum); break; } case LIBNET_PROTO_ISL: { - // struct libnet_isl_hdr *islh_p = - // (struct libnet_isl_hdr *)buf; +#if 0 + struct libnet_isl_hdr *islh_p = + (struct libnet_isl_hdr *)buf; +#endif /* * Need to compute 4 byte CRC for the ethernet frame and for * the ISL frame itself. Use the libnet_crc function. @@ -356,7 +529,7 @@ libnet_do_checksum(libnet_t *l, u_int8_t *buf, int pro default: { snprintf(l->err_buf, LIBNET_ERRBUF_SIZE, - "%s(): unsuported protocol %d\n", __func__, protocol); + "%s(): unsupported protocol %d", __func__, protocol); return (-1); } } @@ -364,8 +537,8 @@ libnet_do_checksum(libnet_t *l, u_int8_t *buf, int pro } -u_int16_t -libnet_ip_check(u_int16_t *addr, int len) +uint16_t +libnet_ip_check(uint16_t *addr, int len) { int sum; @@ -373,4 +546,9 @@ libnet_ip_check(u_int16_t *addr, int len) return (LIBNET_CKSUM_CARRY(sum)); } -/* EOF */ +/** + * Local Variables: + * indent-tabs-mode: nil + * c-file-style: "stroustrup" + * End: + */