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>)
18 #include <sys/types.h>
20 #include <netinet/in.h>
22 #include "sendip_module.h"
27 /* Character that identifies our options
29 const char opt_char='t';
31 static void tcpcsum(sendip_data *ip_hdr, sendip_data *tcp_hdr,
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;
39 fprintf(stderr,"Out of memory: TCP checksum not computed\n");
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));
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);
53 tcp->check = csum(buf,12+tcp_hdr->alloc_len+data->alloc_len);
57 static void tcp6csum(sendip_data *ipv6_hdr, sendip_data *tcp_hdr,
59 tcp_header *tcp = (tcp_header *)tcp_hdr->data;
60 ipv6_header *ipv6 = (ipv6_header *)ipv6_hdr->data;
61 struct ipv6_pseudo_hdr phdr;
63 u_int16_t *buf = malloc(sizeof(phdr)+tcp_hdr->alloc_len+data->alloc_len);
64 u_int8_t *tempbuf = (u_int8_t *)buf;
67 fprintf(stderr,"Out of memory: TCP checksum not computed\n");
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;
77 memcpy(tempbuf,&phdr,sizeof(phdr));
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);
84 tcp->check = csum(buf,sizeof(phdr)+tcp_hdr->alloc_len+data->alloc_len);
88 static void addoption(u_int8_t opt, u_int8_t len, u_int8_t *data,
90 pack->data = realloc(pack->data, pack->alloc_len + len);
91 *((u_int8_t *)pack->data+pack->alloc_len) = opt;
93 *((u_int8_t *)pack->data+pack->alloc_len+1)=len;
95 memcpy((u_int8_t *)pack->data+pack->alloc_len+2,data,len-2);
96 pack->alloc_len += len;
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;
109 bool do_opt(char *opt, char *arg, sendip_data *pack) {
110 tcp_header *tcp = (tcp_header *)pack->data;
114 tcp->source = htons((u_int16_t)strtoul(arg, (char **)NULL, 0));
115 pack->modified |= TCP_MOD_SOURCE;
118 tcp->dest = htons((u_int16_t)strtoul(arg, (char **)NULL, 0));
119 pack->modified |= TCP_MOD_DEST;
122 tcp->seq = htonl((u_int32_t)strtoul(arg, (char **)NULL, 0));
123 pack->modified |= TCP_MOD_SEQ;
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)) {
130 pack->modified |= TCP_MOD_ACK;
134 tcp->off = (u_int16_t)strtoul(arg, (char **)NULL, 0) &0xF;
135 pack->modified |= TCP_MOD_OFF;
138 tcp->res = (u_int16_t)(strtoul(arg, (char **)NULL, 0) & 0x000F);
139 pack->modified |= TCP_MOD_RES;
144 tcp->ecn=(u_int16_t)*arg&1;
145 pack->modified |= TCP_MOD_ECN;
148 tcp->cwr=(u_int16_t)*arg&1;
149 pack->modified |= TCP_MOD_CWR;
152 tcp->urg=(u_int16_t)*arg&1;
153 pack->modified |= TCP_MOD_URG;
156 tcp->ack=(u_int16_t)*arg&1;
157 pack->modified |= TCP_MOD_ACK;
160 tcp->psh=(u_int16_t)*arg&1;
161 pack->modified |= TCP_MOD_PSH;
164 tcp->rst=(u_int16_t)*arg&1;
165 pack->modified |= TCP_MOD_RST;
168 tcp->syn=(u_int16_t)*arg&1;
169 pack->modified |= TCP_MOD_SYN;
172 tcp->fin=(u_int16_t)*arg&1;
173 pack->modified |= TCP_MOD_FIN;
176 usage_error("TCP flag not known\n");
181 tcp->window = htons((u_int16_t)strtoul(arg, (char **)NULL, 0));
182 pack->modified |= TCP_MOD_WINDOW;
185 tcp->check = htons((u_int16_t)strtoul(arg, (char **)NULL, 0));
186 pack->modified |= TCP_MOD_CHECK;
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)) {
193 pack->modified |= TCP_MOD_URG;
199 if(!strcmp(opt+2, "num")) {
200 /* Other options (auto length) */
201 u_int8_t *data = malloc(strlen(arg)+2);
204 fprintf(stderr,"Out of memory!\n");
207 sprintf(data,"0x%s",arg);
208 len = compact_string(data);
210 addoption(*data,1,NULL,pack);
212 addoption(*data,len+1,data+1,pack);
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 */
238 /* count the options */
241 next=strchr(next,',');
246 comb = malloc(count*8);
252 next=strchr(arg, ':');
255 "Value in tcp sack option incorrect. Usage: \n");
257 " -tosack left:right[,left:right...]\n");
264 next=strchr(arg, ',');
266 next=arg-1; /* Finito - next points to \0 */
278 addoption(5,count*8+2,comb,pack);
280 } else if (!strcmp(opt+2, "ts")) {
281 /* Timestamp rfc1323 */
282 u_int32_t tsval=0, tsecr=0;
284 if (2!=sscanf(arg, "%d:%d", &tsval, &tsecr)) {
286 "Invalid value for tcp timestamp option.\n");
288 "Usage: -tots tsval:tsecr\n");
292 memcpy(comb, &tsval, 4);
294 memcpy(comb+4, &tsecr, 4);
295 addoption(8,10,comb,pack);
297 /* Unrecognized -to* */
298 fprintf(stderr, "unsupported TCP Option %s val %s\n",
305 usage_error("unknown TCP option\n");
315 bool finalize(char *hdrs, sendip_data *headers[], sendip_data *data,
317 tcp_header *tcp = (tcp_header *)pack->data;
319 /* Set relevant fields */
320 if(!(pack->modified&TCP_MOD_SEQ)) {
321 tcp->seq = (u_int32_t)rand();
323 if(!(pack->modified&TCP_MOD_OFF)) {
324 tcp->off = (u_int16_t)((pack->alloc_len+3)/4) & 0x0F;
326 if(!(pack->modified&TCP_MOD_SYN)) {
329 if(!(pack->modified&TCP_MOD_WINDOW)) {
330 tcp->window=htons((u_int16_t)65535);
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;
340 if(!(pack->modified&TCP_MOD_CHECK)) {
341 tcpcsum(headers[i],pack,data);
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;
349 if(!(pack->modified&TCP_MOD_CHECK)) {
350 tcp6csum(headers[i],pack,data);
353 if(!(pack->modified&TCP_MOD_CHECK)) {
354 usage_error("TCP checksum not defined when TCP is not embedded in IP\n");
363 return sizeof(tcp_opts)/sizeof(sendip_option);
365 sendip_option *get_opts() {