OSDN Git Service

2013.10.24
[uclinux-h8/uClinux-dist.git] / lib / Libnet / src / libnet_checksum.c
1 /*
2  *  $Id: libnet_checksum.c,v 1.1.1.1 2000/05/25 00:28:49 route Exp $
3  *
4  *  libnet
5  *  libnet_checksum.c - IP checksum routines
6  *
7  *  Copyright (c) 1998 - 2001 Mike D. Schiffman <mike@infonexus.com>
8  *  Copyright (c) 1999, 2000 Dug Song <dugsong@monkey.org>
9  *  All rights reserved.
10  *
11  * Redistribution and use in source and binary forms, with or without
12  * modification, are permitted provided that the following conditions
13  * are met:
14  * 1. Redistributions of source code must retain the above copyright
15  *    notice, this list of conditions and the following disclaimer.
16  * 2. Redistributions in binary form must reproduce the above copyright
17  *    notice, this list of conditions and the following disclaimer in the
18  *    documentation and/or other materials provided with the distribution.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
21  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
24  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30  * SUCH DAMAGE.
31  *
32  */
33
34 #if (HAVE_CONFIG_H)
35 #include "../include/config.h"
36 #endif
37 #include "../include/libnet.h"
38
39 int
40 libnet_in_cksum(u_short *addr, int len)
41 {
42     int sum;
43     int nleft;
44     u_short ans;
45     u_short *w;
46
47     sum = 0;
48     ans = 0;
49     nleft = len;
50     w = addr;
51
52     while (nleft > 1)
53     {
54         sum += *w++;
55         nleft -= 2;
56     }
57     if (nleft == 1)
58     {
59         *(u_char *)(&ans) = *(u_char *)w;
60         sum += ans;
61     }
62     return (sum);
63 }
64
65
66 int
67 libnet_do_checksum(u_char *buf, int protocol, int len)
68 {
69     struct libnet_ip_hdr *iph_p;
70     int ip_hl;
71     int sum;
72
73     sum = 0;
74     iph_p = (struct libnet_ip_hdr *)buf;
75     ip_hl = iph_p->ip_hl << 2;
76
77     /*
78      *  Dug Song came up with this very cool checksuming implementation
79      *  eliminating the need for explicit psuedoheader use.  Check it out.
80      */
81     switch (protocol)
82     {
83         /*
84          *  Style note: normally I don't advocate declaring variables inside
85          *  blocks of control, but it makes good sense here. -- MDS
86          */
87         case IPPROTO_TCP:
88         {
89             struct libnet_tcp_hdr *tcph_p =
90                 (struct libnet_tcp_hdr *)(buf + ip_hl);
91
92 #if (STUPID_SOLARIS_CHECKSUM_BUG)
93             tcph_p->th_sum = tcph_p->th_off << 2;
94             return (1);
95 #endif /* STUPID_SOLARIS_CHECKSUM_BUG */
96
97             tcph_p->th_sum = 0;
98             sum = libnet_in_cksum((u_short *)&iph_p->ip_src, 8);
99             sum += ntohs(IPPROTO_TCP + len);
100             sum += libnet_in_cksum((u_short *)tcph_p, len);
101             tcph_p->th_sum = LIBNET_CKSUM_CARRY(sum);
102             break;
103         }
104         case IPPROTO_UDP:
105         {
106             struct libnet_udp_hdr *udph_p =
107                 (struct libnet_udp_hdr *)(buf + ip_hl);
108
109             udph_p->uh_sum = 0;
110             sum = libnet_in_cksum((u_short *)&iph_p->ip_src, 8);
111             sum += ntohs(IPPROTO_UDP + len);
112             sum += libnet_in_cksum((u_short *)udph_p, len);
113             udph_p->uh_sum = LIBNET_CKSUM_CARRY(sum);
114             break;
115         }
116         case IPPROTO_ICMP:
117         {
118             struct libnet_icmp_hdr *icmph_p =
119                 (struct libnet_icmp_hdr *)(buf + ip_hl);
120
121             icmph_p->icmp_sum = 0;
122             sum = libnet_in_cksum((u_short *)icmph_p, len);
123             icmph_p->icmp_sum = LIBNET_CKSUM_CARRY(sum);
124             break;
125         }
126         case IPPROTO_IGMP:
127         {
128             struct libnet_igmp_hdr *igmph_p =
129                 (struct libnet_igmp_hdr *)(buf + ip_hl);
130
131             igmph_p->igmp_sum = 0;
132             sum = libnet_in_cksum((u_short *)igmph_p, len);
133             igmph_p->igmp_sum = LIBNET_CKSUM_CARRY(sum);
134             break;
135         }
136         case IPPROTO_OSPF:
137         {
138             struct libnet_ospf_hdr *oh_p =
139                 (struct libnet_ospf_hdr *)(buf + ip_hl);
140
141             u_char *payload = (u_char *)(buf + ip_hl + LIBNET_AUTH_H + 
142                         sizeof(oh_p));
143             u_char *tbuf = (u_char *)malloc(sizeof(oh_p) + sizeof(payload));
144             if (tbuf == NULL)
145             {
146                 return (-1);
147             }
148             oh_p->ospf_cksum = 0;
149             sum += libnet_in_cksum((u_short *)tbuf, sizeof(tbuf));
150             oh_p->ospf_cksum = LIBNET_CKSUM_CARRY(sum);
151             free(tbuf);
152             break;
153         }
154         case IPPROTO_OSPF_LSA:
155         {
156             /*
157              *  Reworked fletcher checksum taken from RFC 1008.
158              */
159             int c0, c1;
160             struct libnet_lsa_hdr *lsa_p = (struct libnet_lsa_hdr *)buf;
161             u_char *p, *p1, *p2, *p3;
162
163             c0 = 0;
164             c1 = 0;
165
166             lsa_p->lsa_cksum[0] = 0;
167             lsa_p->lsa_cksum[1] = 0;    /* zero out checksum */
168
169             p = buf;
170             p1 = buf;
171             p3 = buf + len;             /* beginning and end of buf */
172
173             while (p1 < p3)
174             {
175                 p2 = p1 + LIBNET_MODX;
176                 if (p2 > p3)
177                 {
178                     p2 = p3;
179                 }
180   
181                 for (p = p1; p < p2; p++)
182                 {
183                     c0 += (*p);
184                     c1 += c0;
185                 }
186
187                 c0 %= 255;
188                 c1 %= 255;      /* modular 255 */
189  
190                 p1 = p2;
191             }
192
193             lsa_p->lsa_cksum[0] = (((len - 17) * c0 - c1) % 255);
194             if (lsa_p->lsa_cksum[0] <= 0)
195             {
196                 lsa_p->lsa_cksum[0] += 255;
197             }
198
199             lsa_p->lsa_cksum[1] = (510 - c0 - lsa_p->lsa_cksum[0]);
200             if (lsa_p->lsa_cksum[1] > 255)
201             {
202                 lsa_p->lsa_cksum[1] -= 255;
203             }
204             break;
205         }
206         case IPPROTO_IP:
207         {
208             iph_p->ip_sum = 0;
209             sum = libnet_in_cksum((u_short *)iph_p, len);
210             iph_p->ip_sum = LIBNET_CKSUM_CARRY(sum);
211             break;
212         }
213         case IPPROTO_VRRP:
214         {
215             struct libnet_vrrp_hdr *vrrph_p =
216                 (struct libnet_vrrp_hdr *)(buf + ip_hl);
217
218             vrrph_p->vrrp_sum = 0;
219             sum = libnet_in_cksum((u_short *)vrrph_p, len);
220             vrrph_p->vrrp_sum = LIBNET_CKSUM_CARRY(sum);
221             break;
222         }
223         default:
224         {
225 #if (__DEBUG)
226             libnet_error(LN_ERR_CRITICAL, "do_checksum: UNSUPP protocol %d\n",
227                     protocol);
228 #endif
229             return (-1);
230         }
231     }
232     return (1);
233 }
234
235
236 u_short
237 libnet_ip_check(u_short *addr, int len)
238 {
239     int sum;
240
241     sum = libnet_in_cksum(addr, len);
242     return (LIBNET_CKSUM_CARRY(sum));
243 }
244
245 /* EOF */