0x01 简介

TCP头部和IPV4头部除了固定的20字节外,都设置了 OPTION 字段用于存储自定义的数据,因为TCP头部和IPV4的报文长度字段均为4字节,所表示的最大值为15, 乘4,报文头部最大长度为60字节,因此Option字段最大长度为40字节,足够存储大量的报文控制信息。TCP和IPV4 OPTION的格式均为(标识字段 - 长度 - 数据)格式,一般采取4字节对齐存储。

目前 IP Option应用场景较少,且公网路由器对 IP Option的检查较为严格,一般都会直接丢弃带有 IP Option 的报文。TCP Option 的应用场景则较为广泛,常见的包括 TimeStamp(应用于时延测量), TCP_Window_Scalling(长肥网络下,TCP接收窗口需要足够大才能达到瓶颈带宽,此时需要 Window_Scaling 来表示一个更大的接收窗口),TCP_SACK(选择性确认,可以大幅提高TCP在丢包时的性能)等等,这些选项一般都会默认开启,路由器、端主机对这些选项的支持度也较高。本文主要介绍 TCP & IPV4 Option的处理逻辑,然后介绍一种通过 IP Option 字段在内网传输报文控制数据的方法。

0x02 TCP OPTION Linux实现

步骤1: 构造TCP Option,计算存储空间

Linux把TCP选项的处理逻辑分为了SYN报文的选项和普通报文的选项两个部分,在TCP报文构造函数 static int tcp_transmit_skb(struct sock *sk, struct sk_buff *skb, int clone_it, gfp_t gfp_mask) 中有下面这段逻辑

  1. 如果报文为SYN报文,则调用 tcp_syn_options 函数处理握手期间的一些TCP Option,例如MSS协商,Window_Scaling等。
  2. 如果报文为非SYN报文,则调用 TCP_established_options 函数处理包括TimeStamp,SACK等选项的构造。
  3. 构造好的TCP选项并不是直接写入TCP头部,而是存储在 struct tcp_out_options 类型的结构体变量 opts 中。

步骤2:分配 TCP 头部需要的存储空间,包括20个字节的标准头部加上TCP Option的部分。

tcp_options_size 为TCP选项部分的总长度,tcp_transmit_skb 接下来在 skb_push 中为TCP头部分配存储空间。

步骤3 : 向TCP头部写入构造好的TCP Option。

tcp_transmit_skb 接下来通过 tcp_options_write() 函数把构造好的TCP Option 从 opts 中读出并写入TCP报文首部

  

步骤4 : TCP Option 的构造逻辑完成,报文进入IP层的处理逻辑。

步骤5:TCP Option 的解析在 tcp_parse_options() 函数 中完成,协议栈在接收到TCP报文后,会调用该函数完成报文头部的OPTION字段的解析。


0x03 IP OPTION Linux实现

IP Option的构造与TCP  Option类似。

步骤1:分配存储空间,在IP报文构造函数 ip_queue_xmit 中,有下面这一段逻辑,分配IP报文头部空间和OPTION字段的存储空间

      

步骤2 : 构造OPTION字段,具体逻辑在 ip_options_build 中,与 TCP Option的逻辑类似。

步骤3: 解析, IP Option的解析在 ip_options_compile 中完成

0x04 应用

在我们的应用场景下,例如向路由器通告一些信息等,报文除了传输数据外,还要传输一些控制信息,我们就是通过IP报头的OPTION字段来携带这些控制信息。一般情况下,带有TCP选项的报文,即使TCP选项是自定义的,公网路由器也不会轻易丢包,但公网路由器对IP选项的审查要严格的多,带有IP选项的报文。当报文从局域网转发到公网的时候,Linux网关可以在Qdisc中删除该选项,并转发到公网,Linux上发送一个带有自定义IP选项的报文也非常容易,不需要修改内核,只需要在用户态用 setsockopt() 调用便可以完成,下面是一个设置自定义IP 选项的Linux套接字客户端程序示例。

 #include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <netinet/ip.h> #define SERV_PORT 1234
#define SERV_IP "127.0.0.1"
#define MAXLINE 4096
#define MAXSIZE 40 #define IPOPT_TAG 0x21 //IP选项标志字段
#define IPOPT_LEN 8 //IP选项长度字段 int main(int argc,char *argv[])
{
int sockfd;
struct sockaddr_in servaddr; memset(&servaddr,,sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = inet_addr(SERV_IP);
servaddr.sin_port = htons(SERV_PORT); //构造自定义的TCP选项
unsigned char opt[MAXSIZE];
opt[] = IPOPT_TAG;
opt[] = IPOPT_LEN;
//写入选项数据
*(int *)(opt + ) = htonl(); if((sockfd = socket(AF_INET,SOCK_STREAM,)) <= ){
perror("socket error : ");
exit();
} if(connect(sockfd,(struct sockaddr *)&servaddr,sizeof(servaddr)) < ){
perror("connect error ");
exit();
} //设置套接字发送该选项
if(setsockopt(sockfd,IPPROTO_IP,IP_OPTIONS,(void *)opt,IPOPT_LEN) < ){
perror("setsockopt error ");
exit();
} char buff[MAXLINE]; while(fgets(buff,MAXLINE,stdin) != NULL){
if(write(sockfd,buff,strlen(buff)) < strlen(buff)){
perror("write error ");
exit();
}
} close(sockfd);
}

内核并没有检测 setsockopt() 的参数,直接将自定义的选项复制到了IP报文选项部分。

我们可以通过 getsockopt() 函数可以直接读取自定义的IP OPTION。

TCP自定义选项的设置和读取相对于IP选项要麻烦一些,一般向TimeStamp,SACK等TCP选项并不需要用户去读取,因此也没有开放用户层访问的接口,直接通过 getsockopt() 函数无法读取自定义的TCP 选项,但我们可以通过修改内核 getsockopt() 来实现自定义TCP选项的读取,我们知道内核 getsockopt()  函数底层是由 do_ip_getsockopt 和 do_tcp_getsockopt 等协议相关的接口组成的, 在 do_tcp_getsockopt 接口内我们可以添加用户层访问自定义TCP选项的接口,然后便可以在用户层通过 getsockopt()  函数来访问自定义的 TCP 选项了。

0x05 总结

TCP Option字段对于提升TCP性能有较大意义,因此需要了解常见的TCP Option字段的含义、开启和关闭。IP Option字段一般来说不容易碰见,但在一些特殊的应用场景下,例如在局域网内捎带报文控制数据还是很有用处的。

0x06 参考

Linux  Kernel  4.12.13    https://elixir.bootlin.com/linux/v4.12.13/source

TCP/IP OPTION字段的更多相关文章

  1. TCP/IP option data aligement issue cause system broken

    1      Problem Description The field reports show that xxx panel will lockup and then reboot while d ...

  2. TCP/IP协议栈--IP首部选项字段的分析

    IP输入函数(ipintr)将在验证分组格式(检验和,长度等)之后.确定分组是否到达目的地之前,对选项进行处理. 这表明分组所 遇到的每一个路由器以及终于的目的主机都对要分组的选项进行处理. IP分组 ...

  3. TCP/IP Four Layer Protocol Format Learning

    相关学习资料 tcp-ip详解卷1:协议.pdf 目录 . 引言 . 应用层 . 传输层 . 网络层 0. 引言 协议中的网络字节序问题 在学习协议格式之前,有一点必须明白,否则我们在观察抓包数据的时 ...

  4. 计算机网络及TCP/IP知识点(全面,慢慢看)

    TCP/IP网络知识点总结 一.总述 1.定义:计算机网络是一些互相连接的.自治的计算机的集合.因特网是网络的网络. 2.分类: 根据作用范围分类: 广域网 WAN (Wide Area Networ ...

  5. TCP/IP协议头部结构体(网摘小结)(转)

    源:TCP/IP协议头部结构体(网摘小结) TCP/IP协议头部结构体(转) 网络协议结构体定义 // i386 is little_endian. #ifndef LITTLE_ENDIAN #de ...

  6. TCP/IP 笔记 - 防火墙和网络地址转换

    防火墙是位于内部网和外部网之间的屏障,是系统的第一套防线,作用是防止非法用户的进入. 网络地址转换是一种IP数据包通过路由器或防火墙时通过重写来源IP地址或目的地址的技术,可以用来隐藏或保护内部网络, ...

  7. TCP/IP网络知识点总结

    学完了计算机网络是时候整理一篇总结了,温故知新.注意:这篇博客很长长长(2.5万字+50图). TCP/IP网络知识点总结 一.总述 1.定义:计算机网络是一些互相连接的.自治的计算机的集合.因特网是 ...

  8. 《TCP/IP 详解 卷1:协议》第 5 章:Internet 协议

    IP 是 TCPIP 协议族中的核心协议.所有 TCP.UDP.ICMP.IGMP 数据都通过 IP 数据包(又称为 packet)来传输.IP 的英文名为 Internet Protocol,是互联 ...

  9. TCP/IP协议的三次握手及实现原理

    TCP/IP是很多的不同的协议组成,实际上是一个协议组,TCP用户数据报表协议(也称作TCP传输控制协议,Transport Control Protocol.可靠的主机到主机层协议.这里要先强调一下 ...

随机推荐

  1. Java基础学习-基本数据类型变量的定义和使用

    注意:如果使用notepad++编码,在cmd控制台编译时报错(编码GBK的不可映射字符),可以参考如下链接进行设置:https://jingyan.baidu.com/article/e3c78d6 ...

  2. Autowired(required=true)

    问题原因 没有实现类的接口上添加了@Autowired注解 问题解决 删掉@Autowired注解 bug详情 Description: Field userDAO in com.crab.servi ...

  3. Python打开新世界的大门-入门篇1

    目录 题记 Python技巧.避坑及心得 八种数据类型 循环 函数 Homework 题外话 之前没有写博客的习惯,现在开始写觉得入门也太晚了吧,看看同龄的大哥都写了十几万字.于是 ...

  4. ios高级开发之多线程(二)NSThread技术

    多线程技术是有多套解决方案的,那么我们该如何选择呢? 技术方案 简介 语言 线程生命周期 使用频率 pthread 1.一套通用的多线程API 2.适用于UNIX,linux,windows等 3.跨 ...

  5. 代码生成器——实现生成pojo,sql,mapper接口

    代码生成器(记录一次兴趣代码,多多指教.转载请标明作者) 在我们开始实现代码生成器之前我们先来对代码生成器有一个简单的了解. 1.什么是代码生成器? 故名思义,也就是生成代码的一个程序.那它是一个什么 ...

  6. webpack学习笔记 (一)

    一.安装nodejs: 点击打开nodejs官方站点: 点击下图框住的按钮,下周nodejs安装包: 安装下载好的安装包. 安装完毕之后,在cmd中输入node -v查看是否已经安装成功  如果有版本 ...

  7. ps使用经验

  8. trueStudio笔记

    1.C标准的选择 可以在项目->属性->C/C++ Build->Setting->Tool Setting->C Compiler->General中选择使用不同 ...

  9. 【官方下载】EasyCMDB官方基础版免费下载使用!

    链接

  10. C# Timer 定时任务

    C#中,Timer是一个定时器,它可以按照指定的时间间隔或者指定的时间执行一个事件. 指定时间间隔是指按特定的时间间隔,如每1分钟.每10分钟.每1个小时等执行指定事件: 指定时间是指每小时的第30分 ...