1 /*
2  * ip6.c
3  *
4  * Copyright (c) 2002 Dug Song <dugsong@monkey.org>
5  *
6  * $Id$
7  */
8 
9 #include "config.h"
10 
11 #include "dnet.h"
12 
13 #define IP6_IS_EXT(n)	\
14 	((n) == IP_PROTO_HOPOPTS || (n) == IP_PROTO_DSTOPTS || \
15 	 (n) == IP_PROTO_ROUTING || (n) == IP_PROTO_FRAGMENT)
16 
17 void
ip6_checksum(void * buf,size_t len)18 ip6_checksum(void *buf, size_t len)
19 {
20 	struct ip6_hdr *ip6 = (struct ip6_hdr *)buf;
21 	struct ip6_ext_hdr *ext;
22 	u_char *p, nxt;
23 	int i, sum;
24 
25 	nxt = ip6->ip6_nxt;
26 
27 	for (i = IP6_HDR_LEN; IP6_IS_EXT(nxt); i += (ext->ext_len + 1) << 3) {
28 		if (i >= (int)len) return;
29 		ext = (struct ip6_ext_hdr *)((u_char *)buf + i);
30 		nxt = ext->ext_nxt;
31 	}
32 	p = (u_char *)buf + i;
33 	len -= i;
34 
35 	if (nxt == IP_PROTO_TCP) {
36 		struct tcp_hdr *tcp = (struct tcp_hdr *)p;
37 
38 		if (len >= TCP_HDR_LEN) {
39 			tcp->th_sum = 0;
40 			sum = ip_cksum_add(tcp, len, 0) + htons(nxt + len);
41 			sum = ip_cksum_add(&ip6->ip6_src, 32, sum);
42 			tcp->th_sum = ip_cksum_carry(sum);
43 		}
44 	} else if (nxt == IP_PROTO_UDP) {
45 		struct udp_hdr *udp = (struct udp_hdr *)p;
46 
47 		if (len >= UDP_HDR_LEN) {
48 			udp->uh_sum = 0;
49 			sum = ip_cksum_add(udp, len, 0) + htons(nxt + len);
50 			sum = ip_cksum_add(&ip6->ip6_src, 32, sum);
51 			if ((udp->uh_sum = ip_cksum_carry(sum)) == 0)
52 				udp->uh_sum = 0xffff;
53 		}
54 	} else if (nxt == IP_PROTO_ICMPV6) {
55 		struct icmp_hdr *icmp = (struct icmp_hdr *)p;
56 
57 		if (len >= ICMP_HDR_LEN) {
58 			icmp->icmp_cksum = 0;
59 			sum = ip_cksum_add(icmp, len, 0) + htons(nxt + len);
60 			sum = ip_cksum_add(&ip6->ip6_src, 32, sum);
61 			icmp->icmp_cksum = ip_cksum_carry(sum);
62 		}
63 	} else if (nxt == IP_PROTO_ICMP || nxt == IP_PROTO_IGMP) {
64 		struct icmp_hdr *icmp = (struct icmp_hdr *)p;
65 
66 		if (len >= ICMP_HDR_LEN) {
67 			icmp->icmp_cksum = 0;
68 			sum = ip_cksum_add(icmp, len, 0);
69 			icmp->icmp_cksum = ip_cksum_carry(sum);
70 		}
71 	}
72 }
73