OSDN Git Service

Fix no pic
[uclinux-h8/uClinux-dist.git] / user / sendip / tcp.c
1 /* tcp.c - tcp support for sendip
2  * Author: Mike Ricketts <mike@earth.li>
3  * TCP options taken from code by Alexander Talos <at@atat.at>
4  * ChangeLog since 2.0 release:
5  * 27/11/2001: Added -tonum option
6  * 02/12/2001: Only check 1 layer for enclosing IPV4 header
7  * ChangeLog since 2.1 release:
8  * 16/04/2002: Tidy up checksum code (like in icmp.c)
9  * 16/04/2002: Add support for TCP over IPV6 (code from armite <armite@163.com>)
10  * 26/08/2002: Fix bug where tcp length was wrong with tcp options
11  * ChangeLog since 2.2 release:
12  * 24/11/2002: made it compile on archs that care about alignment
13  * ChangeLog since 2.4 release:
14  * 21/04/2003: fix errors found by valgrind
15  * 10/06/2003: fix -tonum (pointed out by Yaniv Kaul <ykaul@checkpoint.com>)
16  */
17
18 #include <sys/types.h>
19 #include <stdlib.h>
20 #include <netinet/in.h>
21 #include <string.h>
22 #include "sendip_module.h"
23 #include "tcp.h"
24 #include "ipv4.h"
25 #include "ipv6.h"
26
27 /* Character that identifies our options
28  */
29 const char opt_char='t';
30
31 static void tcpcsum(sendip_data *ip_hdr, sendip_data *tcp_hdr,
32                                                   sendip_data *data) {
33         tcp_header *tcp = (tcp_header *)tcp_hdr->data;
34         ip_header  *ip  = (ip_header *)ip_hdr->data;
35         u_int16_t *buf = malloc(12+tcp_hdr->alloc_len+data->alloc_len);
36         u_int8_t *tempbuf = (u_int8_t *)buf;
37         tcp->check=0;
38         if(tempbuf == NULL) {
39                 fprintf(stderr,"Out of memory: TCP checksum not computed\n");
40                 return;
41         }
42         /* Set up the pseudo header */
43         memcpy(tempbuf,&(ip->saddr),sizeof(u_int32_t));
44         memcpy(&(tempbuf[4]),&(ip->daddr),sizeof(u_int32_t));
45         tempbuf[8]=0;
46         tempbuf[9]=(u_int16_t)ip->protocol;
47         tempbuf[10]=(u_int16_t)((tcp_hdr->alloc_len+data->alloc_len)&0xFF00)>>8;
48         tempbuf[11]=(u_int16_t)((tcp_hdr->alloc_len+data->alloc_len)&0x00FF);
49         /* Copy the TCP header and data */
50         memcpy(tempbuf+12,tcp_hdr->data,tcp_hdr->alloc_len);
51         memcpy(tempbuf+12+tcp_hdr->alloc_len,data->data,data->alloc_len);
52         /* CheckSum it */
53         tcp->check = csum(buf,12+tcp_hdr->alloc_len+data->alloc_len);
54         free(buf);
55 }
56
57 static void tcp6csum(sendip_data *ipv6_hdr, sendip_data *tcp_hdr,
58                                                         sendip_data *data) {
59         tcp_header *tcp = (tcp_header *)tcp_hdr->data;
60         ipv6_header  *ipv6  = (ipv6_header *)ipv6_hdr->data;
61         struct ipv6_pseudo_hdr phdr;
62
63         u_int16_t *buf = malloc(sizeof(phdr)+tcp_hdr->alloc_len+data->alloc_len);
64         u_int8_t *tempbuf = (u_int8_t *)buf;
65         tcp->check=0;
66         if(tempbuf == NULL) {
67                 fprintf(stderr,"Out of memory: TCP checksum not computed\n");
68                 return;
69         }
70
71         /* Set up the pseudo header */
72         memset(&phdr,0,sizeof(phdr));
73         memcpy(&phdr.source,&ipv6->ip6_src,sizeof(struct in6_addr));
74         memcpy(&phdr.destination,&ipv6->ip6_dst,sizeof(struct in6_addr));
75         phdr.ulp_length=IPPROTO_TCP;
76         
77         memcpy(tempbuf,&phdr,sizeof(phdr));
78
79         /* Copy the TCP header and data */
80         memcpy(tempbuf+sizeof(phdr),tcp_hdr->data,tcp_hdr->alloc_len);
81         memcpy(tempbuf+sizeof(phdr)+tcp_hdr->alloc_len,data->data,data->alloc_len);
82
83         /* CheckSum it */
84         tcp->check = csum(buf,sizeof(phdr)+tcp_hdr->alloc_len+data->alloc_len);
85         free(buf);
86 }
87
88 static void addoption(u_int8_t opt, u_int8_t len, u_int8_t *data,
89                                                          sendip_data *pack) {
90         pack->data = realloc(pack->data, pack->alloc_len + len);
91         *((u_int8_t *)pack->data+pack->alloc_len) = opt;
92         if(len > 1)
93                 *((u_int8_t *)pack->data+pack->alloc_len+1)=len;
94         if(len > 2)
95                 memcpy((u_int8_t *)pack->data+pack->alloc_len+2,data,len-2);
96         pack->alloc_len += len;
97 }
98
99 sendip_data *initialize(void) {
100         sendip_data *ret = malloc(sizeof(sendip_data));
101         tcp_header *tcp = malloc(sizeof(tcp_header));
102         memset(tcp,0,sizeof(tcp_header));
103         ret->alloc_len = sizeof(tcp_header);
104         ret->data = (void *)tcp;
105         ret->modified=0;
106         return ret;
107 }
108
109 bool do_opt(char *opt, char *arg, sendip_data *pack) {
110         tcp_header *tcp = (tcp_header *)pack->data;
111         // opt[0]==t
112         switch(opt[1]) {
113         case 's':
114                 tcp->source = htons((u_int16_t)strtoul(arg, (char **)NULL, 0));
115                 pack->modified |= TCP_MOD_SOURCE;
116                 break;
117         case 'd':
118                 tcp->dest = htons((u_int16_t)strtoul(arg, (char **)NULL, 0));
119                 pack->modified |= TCP_MOD_DEST;
120                 break;
121         case 'n':
122                 tcp->seq = htonl((u_int32_t)strtoul(arg, (char **)NULL, 0));
123                 pack->modified |= TCP_MOD_SEQ;
124                 break;
125         case 'a':
126                 tcp->ack_seq = htonl((u_int32_t)strtoul(arg, (char **)NULL, 0));
127                 pack->modified |= TCP_MOD_ACKSEQ;
128                 if(!(pack->modified&TCP_MOD_ACK)) {
129                         tcp->ack = 1;
130                         pack->modified |= TCP_MOD_ACK;
131                 }
132                 break;
133         case 't':
134                 tcp->off = (u_int16_t)strtoul(arg, (char **)NULL, 0) &0xF;
135                 pack->modified |= TCP_MOD_OFF;
136                 break;
137         case 'r':
138                 tcp->res = (u_int16_t)(strtoul(arg, (char **)NULL, 0) & 0x000F);
139                 pack->modified |= TCP_MOD_RES;
140                 break;
141         case 'f':
142                 switch(opt[2]) {
143                 case 'e':
144                         tcp->ecn=(u_int16_t)*arg&1;
145                         pack->modified |= TCP_MOD_ECN;
146                         break;
147                 case 'c':
148                         tcp->cwr=(u_int16_t)*arg&1;
149                         pack->modified |= TCP_MOD_CWR;
150                         break;
151                 case 'u':
152                         tcp->urg=(u_int16_t)*arg&1;
153                         pack->modified |= TCP_MOD_URG;
154                         break;
155                 case 'a':
156                         tcp->ack=(u_int16_t)*arg&1;
157                         pack->modified |= TCP_MOD_ACK;
158                         break;
159                 case 'p':
160                         tcp->psh=(u_int16_t)*arg&1;
161                         pack->modified |= TCP_MOD_PSH;
162                         break;
163                 case 'r':
164                         tcp->rst=(u_int16_t)*arg&1;
165                         pack->modified |= TCP_MOD_RST;
166                         break;
167                 case 's':
168                         tcp->syn=(u_int16_t)*arg&1;
169                         pack->modified |= TCP_MOD_SYN;
170                         break;
171                 case 'f':
172                         tcp->fin=(u_int16_t)*arg&1;
173                         pack->modified |= TCP_MOD_FIN;
174                         break;
175                 default:
176                         usage_error("TCP flag not known\n");
177                         return FALSE;
178                 }
179                 break;
180         case 'w':
181                 tcp->window = htons((u_int16_t)strtoul(arg, (char **)NULL, 0));
182                 pack->modified |= TCP_MOD_WINDOW;
183                 break;
184         case 'c':
185                 tcp->check = htons((u_int16_t)strtoul(arg, (char **)NULL, 0));
186                 pack->modified |= TCP_MOD_CHECK;
187                 break;
188         case 'u':
189                 tcp->urg_ptr = htons((u_int16_t)strtoul(arg, (char **)NULL, 0));
190                 pack->modified |= TCP_MOD_URGPTR;
191                 if(!(pack->modified&TCP_MOD_URG)) {
192                         tcp->urg = 1;
193                         pack->modified |= TCP_MOD_URG;
194                 }
195                 break;
196
197         case 'o':
198                 /* TCP OPTIONS */
199                 if(!strcmp(opt+2, "num")) {
200                         /* Other options (auto length) */
201                         u_int8_t *data = malloc(strlen(arg)+2);
202                         int len;
203                         if(!data) {
204                                 fprintf(stderr,"Out of memory!\n");
205                                 return FALSE;
206                         }
207                         sprintf(data,"0x%s",arg);
208                         len = compact_string(data);
209                         if(len==1)
210                                 addoption(*data,1,NULL,pack);
211                         else
212                                 addoption(*data,len+1,data+1,pack);
213                         free(data);
214                 } else if (!strcmp(opt+2, "eol")) {
215                         /* End of options list RFC 793 kind 0, no length */
216                         addoption(0,1,NULL,pack);
217                 } else if (!strcmp(opt+2, "nop")) {
218                         /* No op RFC 793 kind 1, no length */
219                         addoption(1,1,NULL,pack);
220                 } else if (!strcmp(opt+2, "mss")) {
221                         /* Maximum segment size RFC 793 kind 2 */
222                         u_int16_t mss=htons(atoi(arg));
223                         addoption(2,4,(u_int8_t *)&mss,pack);
224                 } else if (!strcmp(opt+2, "wscale")) {
225                         /* Window scale rfc1323 */
226                         u_int8_t wscale=atoi(arg);
227                         addoption(3,3,&wscale,pack);
228                 } else if (!strcmp(opt+2, "sackok")) {
229                         /* Selective Acknowledge permitted rfc1323 */
230                         addoption(4,2,NULL,pack);
231                 } else if (!strcmp(opt+2, "sack")) {
232                    /* Selective Acknowledge rfc1323 */
233                         unsigned char *next;
234                         u_int32_t le, re;
235                         u_int8_t *comb, *c;
236                         int count=0;
237
238                         /* count the options */
239                         next=arg;
240                         while(next) {
241                                 next=strchr(next,',');
242                                 count++;
243                                 if(next) next++;
244                         }
245                         
246                         comb = malloc(count*8);
247                         c = comb;
248                         
249                         next=arg;
250                         while(*next) { 
251                                 /* get left edge */
252                                 next=strchr(arg, ':');
253                                 if (!next) { 
254                                         fprintf(stderr, 
255                                                           "Value in tcp sack option incorrect. Usage: \n");
256                                         fprintf(stderr, 
257                                                           " -tosack left:right[,left:right...]\n");
258                                         return FALSE;
259                                 }
260                                 *next++=0;
261                                 le=atoi(arg);
262                                 arg=next;
263                                 /* get right edge */
264                                 next=strchr(arg, ',');
265                                 if (!next) 
266                                         next=arg-1; /* Finito - next points to \0 */ 
267                                 else
268                                         *next++=0;
269                                 re=atoi(arg);
270                                 arg=next;
271                                 
272                                 le=htonl(le);
273                                 re=htonl(re);
274                                 memcpy(c, &le, 4);
275                                 memcpy(c+4, &re, 4);
276                                 c+=8;
277                         }
278                         addoption(5,count*8+2,comb,pack);
279                         free(comb);
280                 } else if (!strcmp(opt+2, "ts")) {
281                         /* Timestamp rfc1323 */
282                         u_int32_t tsval=0, tsecr=0;
283                         u_int8_t comb[8];
284                         if (2!=sscanf(arg, "%d:%d", &tsval, &tsecr)) {
285                                 fprintf(stderr, 
286                                                   "Invalid value for tcp timestamp option.\n");
287                                 fprintf(stderr, 
288                                                   "Usage: -tots tsval:tsecr\n");
289                                 return FALSE;
290                         }
291                         tsval=htonl(tsval);
292                         memcpy(comb, &tsval, 4);
293                         tsecr=htonl(tsecr);
294                         memcpy(comb+4, &tsecr, 4);
295                         addoption(8,10,comb,pack);
296                 } else {
297                         /* Unrecognized -to* */
298                         fprintf(stderr, "unsupported TCP Option %s val %s\n", 
299                                           opt, arg);
300                         return FALSE;
301                 } 
302                 break;
303                 
304         default:
305                 usage_error("unknown TCP option\n");
306                 return FALSE;
307                 break;
308
309         }
310
311         return TRUE;
312
313 }
314
315 bool finalize(char *hdrs, sendip_data *headers[], sendip_data *data,
316                                   sendip_data *pack) {
317         tcp_header *tcp = (tcp_header *)pack->data;
318         
319         /* Set relevant fields */
320         if(!(pack->modified&TCP_MOD_SEQ)) {
321                 tcp->seq = (u_int32_t)rand();
322         }
323         if(!(pack->modified&TCP_MOD_OFF)) {
324                 tcp->off = (u_int16_t)((pack->alloc_len+3)/4) & 0x0F;
325         }
326         if(!(pack->modified&TCP_MOD_SYN)) {
327                 tcp->syn=1;
328         }
329         if(!(pack->modified&TCP_MOD_WINDOW)) {
330                 tcp->window=htons((u_int16_t)65535);
331         }
332
333         /* Find enclosing IP header and do the checksum */
334         if(hdrs[strlen(hdrs)-1]=='i') {
335                 int i = strlen(hdrs)-1;
336                 if(!(headers[i]->modified&IP_MOD_PROTOCOL)) {
337                         ((ip_header *)(headers[i]->data))->protocol=IPPROTO_TCP;
338                         headers[i]->modified |= IP_MOD_PROTOCOL;
339                 }
340                 if(!(pack->modified&TCP_MOD_CHECK)) {
341                         tcpcsum(headers[i],pack,data);
342                 }
343         } else if(hdrs[strlen(hdrs)-1]=='6') {
344                 int i = strlen(hdrs)-1;
345                 if(!(headers[i]->modified&IPV6_MOD_NXT)) {
346                         ((ipv6_header *)(headers[i]->data))->ip6_nxt=IPPROTO_TCP;
347                         headers[i]->modified |= IPV6_MOD_NXT;
348                 }
349                 if(!(pack->modified&TCP_MOD_CHECK)) {
350                         tcp6csum(headers[i],pack,data);
351                 }
352         } else {
353                 if(!(pack->modified&TCP_MOD_CHECK)) {
354                         usage_error("TCP checksum not defined when TCP is not embedded in IP\n");
355                         return FALSE;
356                 }
357         }
358         
359         return TRUE;
360 }
361
362 int num_opts() {
363         return sizeof(tcp_opts)/sizeof(sendip_option); 
364 }
365 sendip_option *get_opts() {
366         return tcp_opts;
367 }
368 char get_optchar() {
369         return opt_char;
370 }