返回信息流看网上有说tcp的chksum = 0的话 kernel 会自动补全,但是我这里一直是0, 如果加上tcp chksum语句的话,校验值却总是错误的。google了好多文档都没有解决。另外 IP 的那个 chksum 倒是 计算不计算都没什么区别,tcpdump显示都一样,很奇怪的说。 我这里的环境是x86_64 Linux。
tcpdump:
127.0.0.1.22345 > 127.0.0.1.22346: Flags [S], cksum 0x0000 (incorrect -> 0x109c), seq 1732610923, win 0, length 0
加上tcp chksum
127.0.0.1.22345 > 127.0.0.1.22346: Flags [S], cksum 0x0eb9 (incorrect -> 0x109c), seq 1732610923, win 0, length 0
代码:
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/ip.h>
#include <netinet/tcp.h>
#define MAX_PACKET_SIZE 4096
/* function for header checksums */
unsigned short ip_chksum(unsigned short *buf, int nwords)
{
unsigned long sum;
for (sum = 0; nwords > 0; nwords--)
sum += *buf++;
sum = (sum >> 16) + (sum & 0xffff);
sum += (sum >> 16);
return (unsigned short)(~sum);
}
void ip_header_init(struct iphdr *iph)
{
iph->version = 4;
iph->ihl = 5;
iph->tos = 0;
iph->tot_len = sizeof(struct iphdr) + sizeof(struct tcphdr);
iph->id = htonl(54321); /* no need to set this value if we set DF */
iph->frag_off = 0;
iph->ttl = MAXTTL; /* MAXTTL = 255 */
iph->protocol = 6; /* upper layer protocol, TCP */
iph->check = 0;
/* Initial IP */
iph->saddr = inet_addr("127.0.0.1");
iph->daddr = inet_addr("127.0.0.1");
}
void tcp_header_init(struct tcphdr *tcph)
{
tcph->source = htons(22345);
// tcph->dest = htons(22346);
tcph->seq = random();
tcph->ack_seq = 0;
tcph->res2 = 0;
tcph->doff = 5;
tcph->syn = 1;
tcph->window = htonl(65535);
tcph->check = 0;
tcph->urg_ptr = 0;
}
int main(int argc, char *argv[])
{
int sockfd, on =1;
unsigned int dport;
char data[MAX_PACKET_SIZE];
struct iphdr *iph = (struct iphdr *)data;
struct tcphdr *tcph = (struct tcphdr *)(data + sizeof(struct iphdr));
struct sockaddr_in dst;
if(argc != 3) {
fprintf(stderr, "Invalid parameters!\n");
fprintf(stdout, "Usage: %s <target> <port>\n", argv[0]);
exit(1);
}
if ( (sockfd = socket(PF_INET, SOCK_RAW, IPPROTO_TCP)) < 0) {
perror("Socket raw error ");
exit(1);
}
dport = atoi(argv[2]);
dst.sin_family = AF_INET;
dst.sin_port = htons(dport);
dst.sin_addr.s_addr = inet_addr(argv[1]);
/* init ip and tcp headers */
bzero(data, sizeof(data));
ip_header_init(iph);
tcp_header_init(tcph);
iph->daddr = dst.sin_addr.s_addr;
tcph->dest = htons(dport);
iph->check = chksum((unsigned short *)data, iph->tot_len);
// tcph->check = chksum((unsigned short *)tcph, sizeof(struct tcphdr));
if ( setsockopt(sockfd, IPPROTO_IP, IP_HDRINCL, &on, sizeof(on)) < 0) {
perror("setsockopt IP_HDRINCL error");
exit(1);
}
if (sendto(sockfd, data, iph->tot_len, 0, (struct sockaddr *)&dst, \
sizeof(dst)) < 0) {
perror("send to error");
exit(1);
}
return 0;
}
这是一条镜像帖。来源:北邮人论坛 / soft-design / #41694同步于 2011/12/23
该镜像源已超过 30 天没有更新,可能在源站已被删除。
SoftDesign机器人发帖
TCP raw socket chksum 死活不对,求助!
leoest
2011/12/23镜像同步8 回复
订阅后,新回复会通过你的通知中心匿名送达。
8 条回复
没太研究过tcp
udp的checksum倒是设为0 是不检查
tcp的checksum我记得又相应的函数啊 就算找不到,那个算法随便网上一搜就有,你自己手动加上试试
【 在 leoest (酹江月) 的大作中提到: 】
: 看网上有说tcp的chksum = 0的话 kernel 会自动补全,但是我这里一直是0, 如果加上tcp chksum语句的话,校验值却总是错误的。google了好多文档都没有解决。另外 IP 的那个 chksum 倒是 计算不计算都没什么区别,tcpdump显示都一样,很奇怪的说。 我这里的环境是x86_64 L
: tcpdump:
: 127.0.0.1.22345 > 127.0.0.1.22346: Flags [S], cksum 0x0000 (incorrect -> 0x109c), seq 1732610923, win 0, length 0
: ...................
【 在 purevirtual 的大作中提到: 】
: 没太研究过tcp
: udp的checksum倒是设为0 是不检查
: tcp的checksum我记得又相应的函数啊 就算找不到,那个算法随便网上一搜就有,你自己手动加上试试
: ...................
网上找了好几种算法了,都不行,而且有的说需要校验,有的说设置为0让内核校验,但是没一个成功的。不知道是哪里出的问题。
只能用最笨的方法了 你有那本andrew的经典教材 计算机网络吗?那里面的checksum算法绝对是正确的 你就按照那个手敲就去吧
我这还要赶论文和准备考试 要是平时就帮你找找了
【 在 leoest (酹江月) 的大作中提到: 】
: 网上找了好几种算法了,都不行,而且有的说需要校验,有的说设置为0让内核校验,但是没一个成功的。不知道是哪里出的问题。
【 在 purevirtual 的大作中提到: 】
: 只能用最笨的方法了 你有那本andrew的经典教材 计算机网络吗?那里面的checksum算法绝对是正确的 你就按照那个手敲就去吧
: 我这还要赶论文和准备考试 要是平时就帮你找找了
: 【 在 leoest (酹江月) 的大作中提到: 】
: ...................
下载了你说的这本书,没找到……但是我用过Richard Stevens UNIX网络编程 的checksum算法,还是有问题……不知道是不是我的代码也有问题
不知道了 按理说他库里提供的就可以用
lz解决了别忘了更新一下
【 在 leoest (酹江月) 的大作中提到: 】
: 下载了你说的这本书,没找到……但是我用过Richard Stevens UNIX网络编程 的checksum算法,还是有问题……不知道是不是我的代码也有问题
【 在 purevirtual 的大作中提到: 】
: 不知道了 按理说他库里提供的就可以用
: lz解决了别忘了更新一下
: 【 在 leoest (酹江月) 的大作中提到: 】
: ...................
终于解决了,原来由于TCP首部中不包含源地址与目标地址等信息,为了保证TCP校验的有效性,在进行TCP校验和的计算时,需要增加一个TCP伪首部的校验和,定义如下
struct pseudo_header
{
unsigned int source_address;
unsigned int dest_address;
unsigned char placeholder;
unsigned char protocol;
unsigned short tcp_length;
struct tcphdr tcp;
};
然后将这个伪首部填充并计算校验和放入TCP校验和那里就搞定啦,哈哈!
TCPDUMP抓包
23:46:48.946735 IP 127.0.0.1.italk > 127.0.0.1.22345: Flags [S], seq 0, win 4096, length 0
23:46:48.946766 IP 127.0.0.1.22345 > 127.0.0.1.italk: Flags [S.], seq 157357738, ack 1, win 32792, options [mss 16396], length 0
23:46:48.946783 IP 127.0.0.1.italk > 127.0.0.1.22345: Flags [R], seq 1, win 0, length 0
最后client端会发出reset,为什么出现这种情况大家可以自己google一下,呵呵。
最后附上改好后的程序
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/ip.h>
#include <netinet/tcp.h>
#define MAX_PACKET_SIZE 4096
//needed for checksum calculation
struct pseudo_header {
unsigned int saddr;
unsigned int daddr;
unsigned char placeholder;
unsigned char protocol;
unsigned short tcp_length;
struct tcphdr tcp;
};
/* function for header checksums */
unsigned short cal_chksum(unsigned short *addr,int len)
{ int nleft=len;
int sum=0;
unsigned short *w=addr;
unsigned short answer=0;
while(nleft>1)
{ sum+=*w++;
nleft-=2;
}
if( nleft==1)
{ *(unsigned char *)(&answer)=*(unsigned char *)w;
sum+=answer;
}
sum=(sum>>16)+(sum&0xffff);
sum+=(sum>>16);
answer=~sum;
return answer;
}
void ip_header_init(struct iphdr *iph)
{
iph->version = 4;
iph->ihl = 5;
iph->tos = 0;
iph->tot_len = sizeof(struct iphdr) + sizeof(struct tcphdr);
iph->id = htons(54321); /* no need to set this value if we set DF */
iph->frag_off = 0;
iph->ttl = MAXTTL; /* MAXTTL = 255 */
iph->protocol = 6; /* upper layer protocol, TCP */
iph->check = 0;
/* Initial IP */
iph->saddr = inet_addr("127.0.0.1");
iph->daddr = inet_addr("127.0.0.1");
}
void tcp_header_init(struct tcphdr *tcph)
{
tcph->source = htons(12345);
tcph->dest = htons(22345);
tcph->seq = 0 ;
tcph->ack_seq = 0;
tcph->res2 = 0;
tcph->doff = 5;
tcph->fin = 0;
tcph->syn = 1;
tcph->rst = 0;
tcph->psh = 0;
tcph->ack = 0;
tcph->urg = 0;
tcph->window = htons(4096);
tcph->check = 0;
tcph->urg_ptr = 0;
}
int main(int argc, char *argv[])
{
int sockfd, on =1;
unsigned int dport;
char data[MAX_PACKET_SIZE];
struct iphdr *iph = (struct iphdr *)data;
struct tcphdr *tcph = (struct tcphdr *)(data + sizeof(struct iphdr));
struct sockaddr_in dst;
struct pseudo_header psh;
if(argc != 3) {
fprintf(stderr, "Invalid parameters!\n");
fprintf(stdout, "Usage: %s <target> <port>\n", argv[0]);
exit(1);
}
if ( (sockfd = socket(PF_INET, SOCK_RAW, IPPROTO_TCP)) < 0) {
perror("Socket raw error ");
exit(1);
}
dport = atoi(argv[2]);
dst.sin_family = AF_INET;
dst.sin_port = htons(dport);
dst.sin_addr.s_addr = inet_addr(argv[1]);
/* init ip and tcp headers */
bzero(data, sizeof(data));
ip_header_init(iph);
tcp_header_init(tcph);
iph->daddr = dst.sin_addr.s_addr;
tcph->dest = htons(dport);
/* Don't need set ip chksum , kernel will fill in the correct checksum*/
// iph->check = csum((unsigned short *)data, iph->tot_len);
psh.saddr = iph->saddr;
psh.daddr = iph->daddr;
psh.placeholder = 0;
psh.protocol = IPPROTO_TCP;
/* tcp_len shoud use network bytes , can't directly use sizeof(struct tcphdr) */
psh.tcp_length = htons(sizeof(struct tcphdr));
memcpy(&psh.tcp, tcph, sizeof(struct tcphdr));
tcph->check = cal_chksum((unsigned short *)&psh, sizeof(struct pseudo_header));
if ( setsockopt(sockfd, IPPROTO_IP, IP_HDRINCL, &on, sizeof(on)) < 0) {
perror("setsockopt IP_HDRINCL error");
exit(1);
}
if (sendto(sockfd, data, iph->tot_len, 0, (struct sockaddr *)&dst, sizeof(dst)) < 0) {
perror("send to error");
exit(1);
}
return 0;
}
参考链接:
http://hi.baidu.com/wwwanq/blog/item/c47426253b3e5c35c995590c.html
http://www.binarytides.com/blog/syn-flood-dos-attack/
下一步目标:实现client ack应答,模拟出tcp三次握手。