005.TCP--拼接TCP头部IP头部,实现TCP三次握手的第一步(Linux,原始套接字)
一.目的:
自己拼接IP头,TCP头,计算效验和,将生成的报文用原始套接字发送出去。
若使用tcpdump能监听有对方服务器的包回应,则证明TCP报文是正确的!
二.数据结构:
TCP首部结构图:
struct tcphdr结构体定义:
struct tcphdr //在#include <netinet/tcp.h>中定义
{
u_int16_t source; //源端口 16位
u_int16_t dest; //目的端口 16位
u_int32_t seq; //序列号 32位
u_int32_t ack_seq; //确认号 32位
# if __BYTE_ORDER == __LITTLE_ENDIAN //若当前环境为小端字节序
u_int16_t res1:; //“保留部分”的前4位
u_int16_t doff:; //数据偏移 4位
u_int16_t fin:; //fin 发送端完成任务
u_int16_t syn:; //syn 同步序号用来发起一个连接。
u_int16_t rst:; //rst 重建连接
u_int16_t psh:; //psh 接收方应该尽快将这个报文段交给应用层
u_int16_t ack:; //ack 确认序号有效
u_int16_t urg:; //urg 紧急指针有效
u_int16_t res2:; //“保留部分”的后两位
# elif __BYTE_ORDER == __BIG_ENDIAN
u_int16_t doff:;
u_int16_t res1:;
u_int16_t res2:;
u_int16_t urg:;
u_int16_t ack:;
u_int16_t psh:;
u_int16_t rst:;
u_int16_t syn:;
u_int16_t fin:;
# else
# error "Adjust your <bits/endian.h> defines"
# endif
u_int16_t window; //窗口 16位
u_int16_t check; //检验和 16位
u_int16_t urg_ptr; //紧急指针 16位
};
struct tcphdr
其中:
1.TCP连接建立的第一步中,需要将SYN=1(SYN的报文段不能携带数据),ACK=0。
2.序列号(seq)的窗口(window)的值(几乎)是任意的
3.注意主机字节序和网络字节序之间的转换(大于1字节的数都需要处理)
三.TCP连接建立过程(3次握手) :
四.代码实现:
/*
============================================================================
Name : test.c
Author : huh
Version :
Copyright : ---notice---
Description : Hello World in C, Ansi-style
============================================================================
*/ #include <sys/types.h>
#include <sys/socket.h>
#include <stdio.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <netinet/ip_icmp.h>
#include <netinet/udp.h>
#include <netinet/tcp.h> #define MAXLINE 1024*50 #define LOCAL_IP "192.168.11.104" //本主机IP
#define LOCAL_PORT 8600 //本主机定义端口
#define DEST_IP "115.239.211.112" //要测试的目的ip(此处为百度ip,以后可能发生变化)
#define DEST_PORT 80 //要测试的目的端口 struct udp_front //tcp(udp)伪首部结构体
{
uint32_t srcip;
uint32_t desip;
u_int8_t zero;
u_int8_t protocol;
u_int16_t len;
}; u_int16_t in_chksum(u_int16_t *addr, int len);
u_int16_t tcp_check(char *sendbuf, int len, const struct udp_front front);
int make_message(char *sendbuf, int send_buf_len, uint32_t src_ip, u_int16_t src_port, uint32_t des_ip, u_int16_t des_port); int main()
{
int raw_sockfd;
int size = *;
char send_message[MAXLINE];
struct sockaddr_in server_address;
//创建原始套接字
raw_sockfd = socket(AF_INET, SOCK_RAW, IPPROTO_UDP);
//创建套接字地址
bzero(&server_address,sizeof(server_address));
server_address.sin_family = AF_INET;
server_address.sin_addr.s_addr = inet_addr(DEST_IP);
//设置套接字为随数据包含IP首部(设置这个选项后需要我们手动写入IP头)
setsockopt(raw_sockfd, IPPROTO_IP, IP_HDRINCL, &size, sizeof(size)); bzero(&send_message, sizeof(send_message));
//拼接完整的TCP数据包(IP头+TCP头+数据)
int mesg_len = make_message(send_message, MAXLINE, inet_addr(LOCAL_IP), LOCAL_PORT, inet_addr(DEST_IP), DEST_PORT);
//将IP数据包发送出去
sendto(raw_sockfd, send_message, mesg_len, , (struct sockaddr *)&server_address, sizeof(server_address));
close(raw_sockfd);
return ;
} //拼接IP数据报
int make_message(char *sendbuf, int send_buf_len, uint32_t src_ip, u_int16_t src_port, uint32_t des_ip, u_int16_t des_port)
{
char message[]; //数据在这里并没有用,为空值
bzero(message, sizeof(message));
//strcpy(message, "hello,world!");
struct iphdr *ip;
ip = (struct iphdr *)sendbuf;
ip->ihl = sizeof(struct iphdr) >> ; //首部长度
ip->version = ; //ip协议版本
ip->tos = ; //服务类型字段
ip->tot_len = ; //总长度
ip->id = htons(); //id值
ip->frag_off = ;
ip->ttl = ;
ip->protocol = IPPROTO_TCP;
ip->check = ; //内核会算相应的效验和
ip->saddr = src_ip;
ip->daddr = des_ip; struct udp_front front;
front.srcip = src_ip;
front.desip = des_ip;
front.len = htons( + strlen(message));
front.protocol = ;
front.zero = ; struct tcphdr *tcp;
tcp = (struct tcphdr *)(sendbuf + sizeof(struct iphdr));
bzero(tcp, sizeof(struct tcphdr *));
tcp->source = htons(src_port); //源端口
tcp->dest = htons(des_port); //目的端口
tcp->seq = htonl(); //随机生成的数
tcp->ack_seq = ; //当ack置0的时候,ack_seq无所谓 tcp->doff = ; //数据偏移(TCP头部字节长度/4)
tcp->res1 = ; //保留字段(4位)
tcp->fin = ; //..用来释放一个连接
tcp->syn = ; //..表示这是一个连接请求
tcp->rst = ; //..用来表示tcp连接是否出现严重差错
tcp->psh = ; //..推送
tcp->ack = ; //..表示是一个连接请求
tcp->urg = ; //..紧急数据标志
tcp->res2 = ; //保留字段(2位)
tcp->window = htons(); //初始窗口值设置 tcp->check = ;
tcp->urg_ptr = ; tcp->check = ; //效验和,效验整个tcp数据报
strcpy((sendbuf++), message); //此处message为空 tcp->check = tcp_check((sendbuf+), +strlen(message), front); ip->tot_len = ( + + strlen(message)); //总长度
printf("ip->tot_len:%d\n",ip->tot_len);
ip->check = in_chksum((unsigned short *)sendbuf, ); return (ip->tot_len);
} //计算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+);
return in_chksum((unsigned short *)str, sizeof(front)+len);
} //效验和算法
uint16_t in_chksum(uint16_t *addr, int len)
{
int nleft = len;
uint32_t sum = ;
uint16_t *w = addr;
uint16_t answer = ;
//把ICMP报头二进制数据以2字节为单位累加起来
while (nleft > )
{
sum += *w++;
nleft -= ;
}
if (nleft == )
{
*(unsigned char *)(&answer) = *(unsigned char *)w;
sum += answer;
}
sum = (sum>>) + (sum&0xffff);
sum += (sum>>);
answer = ~sum;
return answer;
}
tcpdump监听结果:
[root@huh ~]# tcpdump -nn -vvv tcp and host 192.168.11.104
tcpdump: listening on eth0, link-type EN10MB (Ethernet), capture size 65535 bytes
13:24:10.154351 IP (tos 0x0, ttl 128, id 10000, offset 0, flags [none], proto TCP (6), length 40)
192.168.11.104.8600 > 115.239.211.112.80: Flags [S], cksum 0x9394 (correct), seq 100000000, win 65535, length 0
13:24:10.201365 IP (tos 0x0, ttl 128, id 18595, offset 0, flags [none], proto TCP (6), length 44)
115.239.211.112.80 > 192.168.11.104.8600: Flags [S.], cksum 0x5b2f (correct), seq 258283074, ack 100000001, win 64240, options [mss 1460], length 0
13:24:10.201444 IP (tos 0x0, ttl 64, id 0, offset 0, flags [DF], proto TCP (6), length 40)
192.168.11.104.8600 > 115.239.211.112.80: Flags [R], cksum 0x9391 (correct), seq 100000001, win 0, length 0
^C
3 packets captured
3 packets received by filter
0 packets dropped by kernel
115.239.211.112:80端口的服务确实回应了,证明我们构造的TCP包没有问题。
注:图片来自谢希仁老师的《计算机网络》课件
005.TCP--拼接TCP头部IP头部,实现TCP三次握手的第一步(Linux,原始套接字)的更多相关文章
- 网络编程 TCP协议:三次握手,四次回收,反馈机制 socket套接字通信 粘包问题与解决方法
TCP协议:传输协议,基于端口工作 三次握手,四次挥手 TCP协议建立双向通道. 三次握手, 建连接: 1:客户端向服务端发送建立连接的请求 2:服务端返回收到请求的信息给客户端,并且发送往客户端建立 ...
- 原始套接字-自定义IP首部和TCP首部
/* ===================================================================================== * * Filenam ...
- 原始套接字的简单tcp包嗅探
原始套接字 sock_raw = socket(AF_INET , SOCK_RAW , IPPROTO_TCP); while(1) { data_size = recvfrom(sock_raw ...
- Linux网络编程——原始套接字实例:MAC 头部报文分析
通过<Linux网络编程——原始套接字编程>得知,我们可以通过原始套接字以及 recvfrom( ) 可以获取链路层的数据包,那我们接收的链路层数据包到底长什么样的呢? 链路层封包格式 M ...
- python使用原始套接字 解析原始ip头数据
使用底层套接字解码底层流量,是这次做的重点工作. 首先来捕获第一个包 # coding:utf-8import socket # 监听的主机IP host = "192.168.1.100& ...
- C++ Win 32 使用原始套接字获取所有ip数据包并分析(包括ping包)
/*页面编码:GBK 开发环境 VS2019 */ #define _WINSOCK_DEPRECATED_NO_WARNINGS#include <iostream>#include&l ...
- 004.UDP--拼接UDP数据包,构造ip头和udp头通信(使用原始套接字)
一.大致流程: 建立一个client端,一个server端,自己构建IP头和UDP头,写入数据(hello,world!)后通过原始套接字(SOCK_RAW)将包发出去. server端收到数据后,打 ...
- 小故事理解TCP/IP连接时的三次握手
在TCP/IP协议中,TCP协议通过三次握手建立一个可靠的连接,示意图如下: 下面通过一个小故事简单理解一下这三次握手的具体含义: 一天,快递员小客(客户端)准备去小服(服务器)家去送快递(准备与服务 ...
- 网络基础二 tcp/ip协议簇 端口 三次握手 四次挥手 11种状态集
第1章 概念介绍 1.1 VLAN 1.1.1 什么是VLAN VLAN(Virtual LAN),翻译成中文是“虚拟局域网”.LAN可以是由少数几台家用计算机构成的网络,也可以是数以百计的计算机构成 ...
随机推荐
- comparator接口与Comparable接口的区别
1. Comparator 和 Comparable 相同的地方 他们都是java的一个接口, 并且是用来对自定义的class比较大小的, 什么是自定义class: 如 public class Pe ...
- wso2esb之代理服务 Proxy Services
代理服务 顾名思义,代理服务充当了WSO2 ESB服务的代理,通常是一个已经存在的服务端点,代理服务可以使用不同的传输方式. 客户可以直接发送请求代理服务的ESB,客户看到服务代理. 运行示例 配置W ...
- SNMPv3/pygal制图/smtplib发邮件
SNMPv3 SNMPv3在路由器端的配置 这个我都没配置过,还得现学现卖这个链接说的是SNMPv3的基本配置 这个链接说的是SNMPv3的view命令,用于管理员可以看到哪些层级的内容 1) 配置 ...
- jQuery10种不同动画效果的响应式全屏遮罩层
遮罩层有很多今天介绍这个jQuery10种不同动画效果的响应式全屏遮罩层 效果预览 下载地址 实例代码 <div class="container"> <head ...
- ScrollMe – 在网页中加入各种滚动动画效果
ScrollMe 是一款 jQuery 插件,用于给网页添加简单的滚动效果.当你向下滚动页面的时候,ScrollMe 可以缩放,旋转和平移页面上的元素.它易于设置,不需要任何自定义的 JavaScri ...
- jQuery Panorama Viewer – 360度全景展示插件
jQuery Panorama Viewer 这款插件可以帮助你在网站中嵌入全景图片.要做到这一点,首先只需要在页面中引入最新的 jQuery 库,以及 jquery.panorama_viewer. ...
- 为你的网页图标(Favicon)添加炫丽的动画和图片
Favico.js 在让你的网页图标显示徽章,图像或视频.你设置可以轻松地在网页图标中使用动画,可以自定义类型的动画,背景颜色和文字颜色.它支持的动画,像幻灯片,渐变,弹出等等. 您可能感兴趣的相关文 ...
- CSS3简单动画
css3的动画确实非常绚丽!浏览器兼容性很重要!. 分享两个小动画 <!doctype html> <html lang="en"> <head> ...
- CSS代码记录
1. 内容横向滚动的代码 .ul { display: box; display: -webkit-box; width: 250px; background: yellow; overflow-y: ...
- animate 实现滑动切换效果
今天和大家分享一下用 animate 实现滑动切换效果的小例子 ------- 来自<一只有梦想的前端小白> 大家都知道jQuery 提供的有一下几种方法能够实现滑动效果: slideDo ...