/* * ip6.c * * Copyright (c) 2002 Dug Song * * $Id$ */ #ifdef _WIN32 #include "dnet_winconfig.h" #else #include "config.h" #endif #include "dnet.h" #include #include #define IP6_IS_EXT(n) \ ((n) == IP_PROTO_HOPOPTS || (n) == IP_PROTO_DSTOPTS || \ (n) == IP_PROTO_ROUTING || (n) == IP_PROTO_FRAGMENT) void ip6_checksum(void *buf, size_t len) { struct ip6_hdr *ip6 = (struct ip6_hdr *)buf; struct ip6_ext_hdr *ext; u_char *p, nxt; int i, sum; nxt = ip6->ip6_nxt; for (i = IP6_HDR_LEN; IP6_IS_EXT(nxt); i += (ext->ext_len + 1) << 3) { if (i >= (int)len) return; ext = (struct ip6_ext_hdr *)((u_char *)buf + i); nxt = ext->ext_nxt; } p = (u_char *)buf + i; len -= i; if (nxt == IP_PROTO_TCP) { struct tcp_hdr *tcp = (struct tcp_hdr *)p; if (len >= TCP_HDR_LEN) { tcp->th_sum = 0; sum = ip_cksum_add(tcp, len, 0) + htons(nxt + len); sum = ip_cksum_add(&ip6->ip6_src, 32, sum); tcp->th_sum = ip_cksum_carry(sum); } } else if (nxt == IP_PROTO_UDP) { struct udp_hdr *udp = (struct udp_hdr *)p; if (len >= UDP_HDR_LEN) { udp->uh_sum = 0; sum = ip_cksum_add(udp, len, 0) + htons(nxt + len); sum = ip_cksum_add(&ip6->ip6_src, 32, sum); if ((udp->uh_sum = ip_cksum_carry(sum)) == 0) udp->uh_sum = 0xffff; } } else if (nxt == IP_PROTO_ICMPV6) { struct icmp_hdr *icmp = (struct icmp_hdr *)p; if (len >= ICMP_HDR_LEN) { icmp->icmp_cksum = 0; sum = ip_cksum_add(icmp, len, 0) + htons(nxt + len); sum = ip_cksum_add(&ip6->ip6_src, 32, sum); icmp->icmp_cksum = ip_cksum_carry(sum); } } else if (nxt == IP_PROTO_ICMP || nxt == IP_PROTO_IGMP) { struct icmp_hdr *icmp = (struct icmp_hdr *)p; if (len >= ICMP_HDR_LEN) { icmp->icmp_cksum = 0; sum = ip_cksum_add(icmp, len, 0); icmp->icmp_cksum = ip_cksum_carry(sum); } } } ssize_t ip6_add_option(void *buf, size_t len, int proto, const void *optbuf, size_t optlen) { struct ip6_hdr *ip6; struct tcp_hdr *tcp = NULL; u_char *p; int hl, datalen, padlen; if (proto != IP_PROTO_TCP) { errno = EINVAL; return (-1); } ip6 = (struct ip6_hdr *)buf; p = (u_char *)buf + IP6_HDR_LEN; tcp = (struct tcp_hdr *)p; hl = tcp->th_off << 2; p = (u_char *)tcp + hl; datalen = ntohs(ip6->ip6_plen) + IP6_HDR_LEN - (p - (u_char *)buf); /* Compute padding to next word boundary. */ if ((padlen = 4 - (optlen % 4)) == 4) padlen = 0; /* XXX - IP_HDR_LEN_MAX == TCP_HDR_LEN_MAX */ if (hl + optlen + padlen > IP_HDR_LEN_MAX || ntohs(ip6->ip6_plen) + IP6_HDR_LEN + optlen + padlen > len) { errno = EINVAL; return (-1); } /* Shift any existing data. */ if (datalen) { memmove(p + optlen + padlen, p, datalen); } /* XXX - IP_OPT_NOP == TCP_OPT_NOP */ if (padlen) { memset(p, IP_OPT_NOP, padlen); p += padlen; } memmove(p, optbuf, optlen); p += optlen; optlen += padlen; tcp->th_off = (p - (u_char *)tcp) >> 2; ip6->ip6_plen = htons(ntohs(ip6->ip6_plen) + optlen); return (optlen); }