源碼編譯方法:gcc -o syn syn.c
結果:在CentOS 6上成功運行,用tcpdump抓包分析,發送的對端有syn,ack包返回,一切正常。
過程:寫代碼時忘記了對tcph->protocol賦值,計算出得checksum老不對,數據包是成功發出去了,但是對端沒syn,ack包回,查了幾個小時,郁悶死我~~~
疑問:ip->check為0是內核會計算ip頭的checksum,但是計算出得結果和我用ip_fast_csum得到的結果不一致,這是為何?標記一下,有結論再附上。
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- #include <getopt.h>
-
- #include <netinet/ip.h>
- #include <netinet/tcp.h>
-
- /* 數據包最大長度,無負載 */
- #define MAX_PKG_SIZE 80 /* ip header = 20 ~ 40, tcp header = 20 ~ 40; max = 80 */
- /* 計算TCP校驗和的最大使用長度 www.linuxidc.com*/
- #define MAX_PSD_SIZE 52 /* psd header = 12, tcp header = 20 ~ 40; max = 52 */
- /* 數據包構造參數 */
- struct st_args{
- struct sockaddr_in saddr; /* 源地址 */
- struct sockaddr_in daddr; /* 目的地址 */
- };
-
- /* 用於計算TCP校驗和的偽頭部 */
- struct psdhdr{
- uint32_t saddr; /* ip頭部源地址 */
- uint32_t daddr; /* ip頭部目的地址 */
- uint8_t mbz; /* 補全字段,需為0 */
- uint8_t protocol; /* ip頭部協議 */
- uint16_t tcpl; /* tcp長度,包括頭部和數據部分 */
- };
-
-
- /*
- * 采用匯編計算ip頭部校驗和
- * @param[in]: iph, ip頭指針;
- * @param[in]: ihl, ip頭長度(4的倍數)
- *
- * @return 16位校驗和
- * */
- static inline uint16_t ip_fast_csum(const void *iph, unsigned int ihl)
- {
- unsigned int sum;
-
- asm(" movl (%1), %0\n"
- " subl $4, %2\n"
- " jbe 2f\n"
- " addl 4(%1), %0\n"
- " adcl 8(%1), %0\n"
- " adcl 12(%1), %0\n"
- "1: adcl 16(%1), %0\n"
- " lea 4(%1), %1\n"
- " decl %2\n"
- " jne 1b\n"
- " adcl $0, %0\n"
- " movl %0, %2\n"
- " shrl $16, %0\n"
- " addw %w2, %w0\n"
- " adcl $0, %0\n"
- " notl %0\n"
- "2:"
- /* Since the input registers which are loaded with iph and ihl
- are modified, we must also specify them as outputs, or gcc
- will assume they contain their original values. */
- : "=r" (sum), "=r" (iph), "=r" (ihl)
- : "1" (iph), "2" (ihl)
- : "memory");
- return (uint16_t)sum;
- }
-
- /*
- * 計算校驗和
- * @param[in]: buffer, 待計算數據指針
- * @param[in]: size, 數據長度
- *
- * @return 校驗和
- * */
- uint16_t csum(uint16_t *buffer, int size)
- {
- unsigned long cksum = 0;
-
- while(size>1)
- {
- cksum += *buffer++;
- size -= sizeof(uint16_t);
- }
-
- if(size)
- {
- cksum += *(unsigned char*)buffer;
- }
-
- cksum = (cksum>>16) + (cksum&0xffff);
- cksum += (cksum>>16);
-
- return (uint16_t)(~cksum);
- }
- /*
- * 調試用的函數,用於輸出數據
- * */
- void data_dump(uint8_t *pdata, int len)
- {
- int i = 0;
- printf("len = %d\n", len);
- for(i = 0; i < len; ++i)
- {
- printf("%02X ", *(pdata + i));
- if((i + 1) % 4 == 0)
- printf("\n");
- }
- }
-
- /*
- * 數據包發送函數,www.linuxidc.com只構造了ip頭+tcp頭大小的長度;
- * ip頭和tcp都無選項部分,tcp無負載數據
- * @param[in]: parg, 構造數據包是采用的一些參數
- *
- * @return -1, 發送失敗;0, 發送成功
- * */
- int send_pkg(struct st_args* parg)
- {
- uint8_t datagram[MAX_PKG_SIZE] = {0};
- uint8_t psdheader[MAX_PSD_SIZE] = {0};
-
- struct iphdr *iph = (struct iphdr*)datagram;
- struct tcphdr *tcph = (struct tcphdr*)(datagram + sizeof(struct iphdr));
- struct tcp_options *tcpopt = (struct tcp_options*)(datagram + sizeof(struct iphdr)
- + sizeof(struct tcphdr));
- struct psdhdr *psdh = (struct psdhdr*)psdheader;
- struct tcphdr *tcph_psd = (struct tcphdr*)(psdheader + sizeof(struct psdhdr));
-
- int sockfd = -1, ret = 0;
- int optval = 1;
- const int *poptval = &optval;
-
- sockfd = socket(PF_INET, SOCK_RAW, IPPROTO_TCP);
- if(sockfd < 0)
- {
- perror("create socket failed!\n");
- goto err_out;
- }
-
- iph->ihl = 5; /* header length, 5 * 4 = 20 Bytes */
- iph->version = 4; /* version, ipv4 */
- iph->tos = 0; /* type of service, gernarel */
- iph->tot_len = sizeof(struct iphdr) + sizeof(struct tcphdr); /* total length, iph + tcph = 32 Bytes */
- iph->id = htons(54321); /* identifcation */
- iph->frag_off = htons(0x02 << 13); /* fragment offset field */
- iph->ttl = 64; /* time to live */
- iph->protocol = 6; /* protocol, tcp */
- iph->check = 0; /* checksum */
- iph->saddr = parg->saddr.sin_addr.s_addr; /* source address */
- iph->daddr = parg->daddr.sin_addr.s_addr; /* dest address */
-
- tcph->source = parg->saddr.sin_port; /* source port */
- tcph->dest = parg->daddr.sin_port; /* dest port */
- tcph->seq = random(); /* current sended packet sequence number */
- tcph->ack_seq = 0; /* expect received next packet sequence number */
- tcph->doff = sizeof(struct tcphdr) / 4; /* data position in the packet */
- tcph->syn = 1; /* syn packet */
- tcph->window = htons(65535); /* size of tcp window, FreeBSD uses this value */
- tcph->check = 0; /* checksum */
- tcph->urg_ptr = 0; /* urgent data position */
-
- psdh->saddr = iph->saddr;
- psdh->daddr = iph->daddr;
- psdh->mbz = 0;
- psdh->protocol = iph->protocol;
- psdh->tcpl = htons(sizeof(struct tcphdr));
- //data_dump(psdheader, sizeof(struct psdhdr));
-
- memcpy(tcph_psd, tcph, sizeof(struct tcphdr));
-
- tcph->check = csum((uint16_t*)psdheader, sizeof(struct psdhdr) + sizeof(struct tcphdr));
- /* iph->check == 0時, 內核會自動計算校驗和 */
- //iph->check = ip_fast_csum(datagram, iph->ihl);
-
- if(setsockopt(sockfd, IPPROTO_IP, IP_HDRINCL, poptval, sizeof(optval)) < 0)
- {
- perror("setsockopt failed!");
- goto err_out;
- }
-
- ret = sendto(sockfd, datagram, iph->tot_len, 0,
- (struct sockaddr*)&(parg->daddr), sizeof(parg->daddr));
- if(ret < 0)
- {
- perror("sendto socket failed!");
- goto err_out;
- }
-
- //data_dump(datagram, 40);
- close(sockfd);
- return 0;
-
- err_out:
- if(sockfd != -1)
- close(sockfd);
- return -1;
- }
-
- int main(int argc, char **argv)
- {
- struct st_args args;
-
- #define MAX_IP_SIZE 16
- uint8_t sip[MAX_IP_SIZE] = "192.168.1.107";
- uint8_t dip[MAX_IP_SIZE] = "192.168.1.1";
- uint16_t sport = 55555;
- uint16_t dport = 80;
- int8_t arg = 0;
-
- struct option lopts[] = {
- {"saddr", required_argument, 0, 's'},
- {"sport", required_argument, 0, 'p'},
- {"daddr", required_argument, 0, 'd'},
- {"dport", required_argument, 0, 'f'}
- };
- while((arg = getopt_long(argc, argv, "s:p:d:f:", lopts, NULL)) != -1)
- {
- switch(arg)
- {
- case 's':
- memcpy(sip, optarg, MAX_IP_SIZE);
- break;
- case 'p':
- sport = atoi(optarg);
- break;
- case 'd':
- memcpy(dip, optarg, MAX_IP_SIZE);
- break;
- case 'f':
- dport = atoi(optarg);
- break;
- default:
- break;
- }
- }
-
- memset(&args, 0, sizeof(struct st_args));
- inet_pton(AF_INET, sip, (void*)&args.saddr.sin_addr);
- args.saddr.sin_port = htons(sport);
-
- inet_pton(AF_INET, dip, (void*)&args.daddr.sin_addr);
- args.daddr.sin_port = htons(dport);
-
- send_pkg(&args);
- return 0;
- }