本文主要分析:在收到客户端的SYN包时,服务器端是如何解析它所携带的TCP选项,并结合本端情况决定是否予以支持。

内核版本:3.6

Author:zhangskd @ csdn blog

概述

收到客户端的SYN包时,需要全面的解析它携带的TCP选项,这样我们就知道客户端支持哪些选项,如果本端也支持,

那么连接就支持这些TCP选项。这些信息在连接建立的过程中,是保存在连接请求块的(request_sock、inet_request_sock、

tcp_request_sock)。

函数调用路径:

tcp_v4_conn_request

|--> tcp_parse_options

3.6版本Linux内核支持的TCP选项包括:

NOP,用于填充。

Max Segment Size,最大分段大小。

Window Scaling,窗口扩大因子。

SACK Permit,是否允许使用SACK。

SACK,携带SACK块。

Timestamp,时间戳选项。

MD5SIG,MD5签名。

Cookie,Cookie extension,2013年3月已从内核移除。

EXP,Experimental,处于实验阶段的选项,新版本用于Fast Open。

实现

全面解析数据包的TCP选项,并保存到指定的tcp_options_received实例中。

/* Look for tcp options. Normally only called on SYN and SYNACK packets.
* But, this can also be called on packets in the established flow when the fast version below fails.
*/ void tcp_parse_options(const struct sk_buff *skb, struct tcp_options_received *opt_rx, const u8 **hvpp,
int estab, struct tcp_fastopen_cookie *foc)
{
const unsigned char *ptr;
const struct tcphdr *th = tcp_hdr(skb);
int length = (th->doff * 4) - sizeof(struct tcphdr); /* 选项的总长度 */ ptr = (const unsigned char *) (th + 1); /* 选项的起始地址 */
opt_rx->saw_tstamp = 0; /* Saw TIMESTAMP on last packet */ while(length > 0) {
int opcode = *ptr++; /* 选项kind */
int opsize; /* 选项length */ switch(opcode) {
case TCPOPT_EOL: /* End of options */
return; case TCPOPT_NOP: /* Padding,填充选项 */
length--; /* 此选项只占一个字节 */
continue; default:
opsize = *ptr++; /* 选项长度 */
if (opsize < 2) /* silly options */
return; /* 选项长度过小 */ if (opsize > length)
return; /* don't parse partial options */ switch(opcode) {
case TCPOPT_MSS: /* MSS选项 */
if (opsize == TCPOLEN_MSS && th->syn && ! estab) {
u16 in_mss = get_unaligned_be16(ptr); if (in_mss) {
/* 如果用户指定了MSS,且比对端通告的小 */
if (opt_rx->user_mss && opt_rx->user_mss < in_mss)
in_mss = opt_rx->user_mss; opt_rx->mss_clamp = in_mss; /* Maximal mss */
}
}
break; case TCPOPT_WINDOW: /* Window scaling */
if (opsize == TCPOLEN_WINDOW && th->syn && ! estab &&
sysctl_tcp_window_scaling) {
__u8 snd_wscale = *(__u8 *)ptr; /* 对端的接收窗口扩大因子 */
opt_rx->wscale_ok = 1; /* 连接使用窗口扩大选项 */ if (snd_wscale > 14) {
net_info_ratelimited("%s: Illegal window scaling value %d >14 received\n",
__func__, snd_wscale);
snd_wscale = 14;
} opt_rx->snd_wscale = snd_wscale; /* 保存对端的接收窗口扩大因子 */
}
break; case TCPOPT_TIMESTAMP: /* Better RTT estimations/PAWS */
if ((opsize == TCPOLEN_TIMESTAMP) && ((estab && opt_rx->tstamp_ok) ||
(!estab && sysctl_tcp_timestamps))) { opt_rx->saw_tstamp = 1; /* 连接支持TIMESTAMP选项 */
opt_rx->rcv_tsval = get_unaligned_be32(ptr); /* Time stamp value */
opt_rx->rcv_tsecr = get_unaligned_be32(ptr + 4); /* Time stamp echo reply */
}
break; case TCPOPT_SACK_PERM: /* SACK Permitted */
if (opsize == TCPOLEN_SACK_PERM && th->syn && !estab && sysctl_tcp_sack) {
opt_rx->sack_ok = TCP_SACK_SEEN; /* 连接支持SACK选项 */
tcp_sack_reset(opt_rx); /* 清空dsack和num_sacks变量 */
}
break; case TCPOPT_SACK: /* SACK Block */
if ((opsize >= (TCPOLEN_SACK_BASE + TCPOLEN_SACK_PERBLOCK)) &&
!((opsize - TCPOLEN_SACK_BASE) % TCPOLEN_SACK_PERBLOCK) && opt_rx->sack_ok) {
/* 保存SACK选项的起始偏移地址 */
TCP_SKB_CB(skb)->sacked = (ptr - 2) - (unsigned char *) th;
}
break; #ifdef CONFIG_TCP_MD5SIG
case TCPOPT_MD5SIG:
/* The MD5 Hash has already been checked
* (see tcp_v{4, 6}_do_rcv()).
*/
break;
#endif /* 注意,在新版内核中此选项已被删除。Cookie extension (experimental) */
case TCPOPT_COOKIE:
/* This option is variable length. */
switch(opsize) {
case TCPOLEN_COOKIE_BASE:
/* not yet implemented */
break; case TCPOLEN_COOKIE_PAIR:
/* not yet implemented */
break; case TCPOLEN_COOKIE_MIN + 0:
case TCPOLEN_COOKIE_MIN + 2:
case TCPOLEN_COOKIE_MIN + 4:
case TCPOLEN_COOKIE_MIN + 6:
case TCPOLEN_COOKIE_MAX: /* 16-bit multple */
opt_rx->cookie_plus = opsize;
*hvpp = ptr;
break; default:
/* ignore option */
break;
}
break; case TCPOPT_EXP: /* Experimental */
/* Fast Open option shares code 254 using a 16 bits magic number.
* It's valid only in SYN or SYN-ACK with an even size.
*/
if (opsize < TCPOLEN_EXP_FASTOPEN_BASE || get_unaligned_be16(ptr) != TCPOPT_FASTOPEN_MAGIC
|| foc == NULL || ! th->syn || (opsize & 1))
break; foc->len = opsize - TCPOLEN_EXP_FASTOPEN_BASE;
if (foc->len >= TCP_FASTOPEN_COOKIE_MIN && foc->len <= TCP_FASTOPEN_COOKIE_MAX)
memcpy(foc->val, ptr + 2, foc->len);
else if (foc->len != 0)
foc->len = -1;
break;
} ptr += opsize - 2;
length -= opsize;
}
}
}
/* TCP options */
#define TCPOPT_NOP 1 /* Padding */
#define TCPOPT_EOL 0 /* End of options */
#define TCPOPT_MSS 2 /* Segment size negotiating */
#define TCPOPT_WINDOW 3 /* Window scaling */
#define TCPOPT_TIMESTAMP 8 /* Better RTT estimations/PAWS */
#define TCPOPT_SACK_PERM 4 /* SACK Permitted */
#define TCPOPT_SACK 5 /* SACK Block */
#define TCPOPT_MD5SIG 19 /* MD5 Signature (RFC2385) */
#define TCPOPT_COOKIE 253 /* Cookie extension (experimental) */
#define TCPOPT_EXP 254 /* Experimental */ /* Magic number to be after the option value for sharing TCP experimental options.
* See draft-ietf-tcpm-experimental-options-00.txt
*/
#define TCPOPT_FASTOPEN_MAGIC 0xF989 /* TCP option lengths */
#define TCPOLEN_MSS 4
#define TCPOLEN_WINDOW 3
#define TCPOLEN_TIMESTAMP 10
#define TCPOLEN_SACK_PERM 2
#define TCPOLEN_COOKIE_BASE 2 /* Cookie-less header extension */
#define TCPOLEN_COOKIE_PAIR 3 /* Cookie pair header extension */
#define TCPOLEN_COOKIE_MIN (TCPOLEN_COOKIE_BASE + TCP_COOKIE_MIN)
#define TCPOLEN_COOKIE_MAX (TCPOLEN_COOKIE_BASE + TCP_COOKIE_MAX)
#define TCPOLEN_EXP_FASTOPEN_BASE 4 /* These are used to set the sack_ok field in struct tcp_options_received */
#define TCP_SACK_SEEN (1 << 0) /* peer is SACK capable */
#define TCP_FACK_ENABLED (1 << 1) /* FACK is enabled locally */
#define TCP_DSACK_SEEN (1 << 2) /* DSACK was received from peer */ /* for TCP_COOKIE_TRANSACTIONS (TCPCT) socket option */
#define TCP_COOKIE_MIN 8 /* 64-bits */
#define TCP_COOKIE_MAX 16 /* 128-bits */ /* TCP Fast Open */
#define TCP_FASTOPEN_COOKIE_MIN 4 /* Min Fast Open Cookie size in bytes */
#define TCP_FASTOPEN_COOKIE_MAX 16 /* Max Fast Open Cookie size in bytes */ /* TCP Fast Open Cookie as stored in memory */
struct tcp_fastopen_cookie {
s8 len;
u8 val[TCP_FASTOPEN_COOKIE_MAX];
}; static inline void tcp_sack_reset(struct tcp_options_received *rx_opt)
{
rx_opt->dsack = 0;
rx_opt->num_sacks = 0;
}

用于判断连接是否使用ECN。要注意的是在建立连接时,要求IP报的ECN域不能被设置,否则就禁用ECN。

/* RFC3168: 6.1.1 SYN packets must not have ECT/ECN bits set.
*
* If we receive a SYN packet with these bits set, it means a network is playing bad games
* with TOS bits. In order to avoid possible false congestion notifications, we disable
* TCP ECN negociation.
*/
static inline void TCP_ECN_create_request(struct request_sock *req, const struct sk_buff *skb)
{
const struct tcphdr *th = tcp_hdr(skb); if (sysctl_tcp_ecn && th->ece && th->cwr && INET_ECN_is_not_ect(TCP_SKB_CB(skb)->ip_dsfield))
inet_rsk(req)->ecn_ok = 1; /* 连接支持ECN */
}

清零TCP选项。

static inline void tcp_clear_options(struct tcp_options_received *rx_opt)
{
rx_opt->tstamp_ok = rx_opt->sack_ok = 0;
rx_opt->wscale_ok = rx_opt->snd_wscale = 0;
rx_opt->cookie_plus = 0;
}

TCP连接建立系列 — TCP选项解析的更多相关文章

  1. TCP连接建立系列 — 服务端接收SYN段

    本文主要分析:服务器端接收到SYN包时的处理路径. 内核版本:3.6 Author:zhangskd @ csdn blog 接收入口 1. 状态为ESTABLISHED时,用tcp_rcv_esta ...

  2. TCP连接建立系列 — 服务端发送SYNACK段

    本文主要分析:服务器端如何构造和发送SYNACK段. 内核版本:3.6 Author:zhangskd @ csdn blog 发送入口 tcp_v4_send_synack()用于发送SYNACK段 ...

  3. TCP连接建立系列 — 服务端接收ACK段(一)

      http://blog.csdn.net/zhangskd/article/details/17923917 分类: Linux TCP/IP Linux Kernel 2014-01-07 09 ...

  4. TCP连接建立系列 — 客户端接收SYNACK和发送ACK

    主要内容:客户端接收SYNACK.发送ACK,完成连接的建立. 内核版本:3.15.2 我的博客:http://blog.csdn.net/zhangskd 接收入口 tcp_v4_rcv |--&g ...

  5. TCP连接建立系列 — 连接请求块

    连接请求块(request_sock)之于TCP三次握手,就如同网络数据包(sk_buff)之于网络协议栈,都是核心的数据结构. 内核版本:3.6 Author:zhangskd @ csdn blo ...

  6. TCP连接建立系列 — 客户端的端口选取和重用

    主要内容:connect()时的端口选取和端口重用. 内核版本:3.15.2 我的博客:http://blog.csdn.net/zhangskd 端口选取 connect()时本地端口是如何选取的呢 ...

  7. TCP连接建立系列 — 客户端发送SYN段

    主要内容:客户端调用connect()时的TCP层实现. 内核版本:3.15.2 我的博客:http://blog.csdn.net/zhangskd connect的TCP层实现 SOCK_STRE ...

  8. TCP连接建立系列 — 服务端接收ACK段(二)

    本文主要分析:三次握手中最后一个ACK段到达时,服务器端的处理路径. 内核版本:3.6 Author:zhangskd @ csdn blog 创建新sock 协议族相关的操作函数,我们要看的是TCP ...

  9. Socket: Java Socket 几个重要的TCP/IP选项解析(转)

    Socket选择可以指定Socket类发送和接受数据的方式.在JDK1.4中共有8个Socket选择可以设置.这8个选项都定义在java.net.SocketOptions接口中.定义如下: publ ...

随机推荐

  1. [django1.6]跑批任务错误(2006, 'MySQL server has gone away')

    有个django的定时任务的需求,调用django的orm来对数据库进行数据处理.  在交互环境下直接启动pyhton脚本没有问题,放在定时任务中时候,总是出现 (2006, 'MySQL serve ...

  2. EJB_开发单表映射的实体bean

    开发单表映射的实体bean 实体bean 它属于java持久化规范(JPA)里的技术,实体bean通过元数据在Javabean和数据库表之间建立起映射关系,然后Java程序员就可以随心所欲的使用面向对 ...

  3. FFmpeg的HEVC解码器源代码简单分析:解析器(Parser)部分

    ===================================================== HEVC源代码分析文章列表: [解码 -libavcodec HEVC 解码器] FFmpe ...

  4. Spark分布式计算和RDD模型研究

    1背景介绍 现今分布式计算框架像MapReduce和Dryad都提供了高层次的原语,使用户不用操心任务分发和错误容忍,非常容易地编写出并行计算程序.然而这些框架都缺乏对分布式内存的抽象和支持,使其在某 ...

  5. 非ROOT实现静默安装的一些思考与体会,AIDL获取IPackageManager,反射ServiceManager,系统签名

    非ROOT实现静默安装的一些思考与体会,AIDL获取IPackageManager,反射ServiceManager,系统签名 最近自家的系统要做一个升级服务,里面有三个功能,第一个是系统升级,也就是 ...

  6. PyCharm 开发Django ,错误汇总

    近期略微接触了一下Django.在学习的过程中可谓是坎坎坷坷,遇到了很多的问题. 下面就来谈一谈我对Django的一点点的见解. Django项目的创建 使用PyCharm来开发Django项目是非常 ...

  7. UNIX网络编程——原始套接字的魔力【上】

    基于原始套接字编程 在开发面向连接的TCP和面向无连接的UDP程序时,我们所关心的核心问题在于数据收发层面,数据的传输特性由TCP或UDP来保证: 也就是说,对于TCP或UDP的程序开发,焦点在Dat ...

  8. Android开发学习之路--MediaPlayer之简单音乐播放器初体验

    很多时候我们都会用手机来播放音乐,播放视频,那么具体地要怎么实现呢,其实主要是MediaPlayer类来完成的.下面通过简单的例子来实现一首歌曲的播放吧.新建工程MediaPlayerStudy,这里 ...

  9. 学习Tensorflow,使用源码安装

    PC上装好Ubuntu系统,我们一步一步来讲解如何使用源码安装tensorflow?(我的Ubuntu系统是15.10) 安装cuda 根据你的系统型号选择相应的cuda版本下载 https://de ...

  10. JAVA之旅(二十八)——File概述,创建,删除,判断文件存在,创建文件夹,判断是否为文件/文件夹,获取信息,文件列表,文件过滤

    JAVA之旅(二十八)--File概述,创建,删除,判断文件存在,创建文件夹,判断是否为文件/文件夹,获取信息,文件列表,文件过滤 我们可以继续了,今天说下File 一.File概述 文件的操作是非常 ...