原始套接字构建 TCP三次握手 及相关问题
分析TCP、IP头部
头部字段
IP头部结构
TCP头部结构
熟悉计算机网络的同学对这些都已经很熟悉了,就不复述了。
Tip: 为了结构清晰 分开的代码可能有一定逻辑性问题,请参照源码
构建TCP伪首部结构体
用于计算校验和
struct udp_front //tcp(udp)伪首部结构体
{
uint32_t srcip; //源IP
uint32_t desip; //目的IP
u_int8_t zero;
u_int8_t protocol; //协议类型
u_int16_t len; //协议长度
};
填充TCP伪首部
//...
struct udp_front front;
front.srcip = ip->saddr;
front.desip = ip->daddr;
front.len = htons(20 + strlen(message));
front.protocol = 6;
front.zero = 0;
填充IP头部
ip = (struct iphdr *)sendbuf;
ip->ihl = sizeof(struct iphdr) >> 2; //首部长度
ip->version = 4; //ip协议版本
ip->tos = 0; //服务类型字段
ip->tot_len = 0; //总长度
ip->id = htons(my_seq); //id值
ip->frag_off = 0;
ip->ttl = 128;
ip->protocol = IPPROTO_TCP;
ip->check = 0; //内核会算相应的效验和
// ip->saddr = src_ip;
//将一个点分十进制的IP转换成一个长整数型数
ip->saddr = inet_addr(argv[1]);
ip->daddr = inet_addr(argv[3]);
//...
ip->tot_len = (20 + 20 + strlen(message)); //IP头长度+TCP头部长度+数据长度 = 总长度
printf("ip->tot_len:%d\n",ip->tot_len);
ip->check = in_chksum((unsigned short *)sendbuf, 20);
填充TCP头部
TCP首部的syn、ack、fin等位 必须按照三次握手的要求构建,第一次请求syn位为1,响应ack位则为1…
struct tcphdr *tcp;
tcp = (struct tcphdr *)(sendbuf + sizeof(struct iphdr));
bzero(tcp, sizeof(struct tcphdr *));
//-------------------------------------------------------------------
//将短整型变量从主机字节顺序转变成网络字节顺序
//htonl 长
//atoi 把字符串转换成整型数
tcp->source = htons(atoi(argv[2])); //源端口
tcp->dest = htons(atoi(argv[4])); //目的端口
tcp->seq = htonl(100000000);
tcp->ack_seq = htonl(ack_seq);
tcp->doff = 5; //数据偏移(TCP头部字节长度/4)
tcp->res1 = 0; //保留字段(4位)
tcp->fin = 0; //..用来释放一个连接
tcp->syn = 1; //..表示这是一个连接请求
tcp->rst = 0; //..用来表示tcp连接是否出现严重差错
tcp->psh = 0; //..推送
tcp->ack = 0; //..表示是一个连接请求
tcp->urg = 0; //..紧急数据标志
tcp->res2 = 0; //保留字段(2位)
tcp->window = htons(65535); //初始窗口值设置
tcp->check = 0;
tcp->urg_ptr = 0;
tcp->check = 0; //效验和,效验整个tcp数据报
strcpy((sendbuf+20+20), message); //把message存入IP+TCP头部位之后
tcp->check = tcp_check((sendbuf+20), 20+strlen(message), front);
计算校验和
想要详细了解的请 参考 博客
//计算tcp(udp)效验和
unsigned short tcp_check(char *sendbuf, int len, const struct udp_front front)
{
char str[MAXLINE];
bzero(&str, MAXLINE);
bcopy(&front, str, sizeof(front));
bcopy(sendbuf, str+sizeof(front), len);
struct udp_front *ptr;
ptr = (struct udp_front *)str;
char *s;
s = (str+20);
return in_chksum((unsigned short *)str, sizeof(front)+len);
}
//效验和算法
uint16_t in_chksum(uint16_t *addr, int len)
{
int nleft = len;
uint32_t sum = 0;
uint16_t *w = addr;
uint16_t answer = 0;
//把ICMP报头二进制数据以2字节为单位累加起来
while (nleft > 1)
{
sum += *