有时候坚持之后,便可得到你最想要的东西
实验目的
基于 packetdrill TCP 三次握手脚本,继续测试 SYN/ACK 中 TCP options MSS 字段的由来,此次构造模拟的是服务器端场景。
基础脚本
# cat tcp_3hs_000.pkt
// TCP 基础之三次握手
0 socket(..., SOCK_STREAM, IPPROTO_TCP) = 3
+0 setsockopt(3, SOL_SOCKET, SO_REUSEADDR, [1], 4) = 0
+0 bind(3, ..., ...) = 0
+0 listen(3, 1) = 0
+0 < S 0:0(0) win 10000 <mss 1460>
+0 > S. 0:0(0) ack 1 <...>
+0.01 < . 1:1(0) ack 1 win 10000
+0 accept(3, ..., ...) = 4
SYN/ACK MSS
SYN/ACK 中 TCP options 的 MSS 实际上也是本地通告的 MSS,代码中是通过 tcp_make_synack 函数确定的,相关的代码流程简要说明如下:
tcp_v4_rcv
|--tcp_v4_do_rcv
| |--tcp_rcv_state_process
| |--tcp_v4_conn_request
| |--tcp_conn_request
| |--tcp_v4_send_synack
| |--tcp_make_synack
tcp_v4_rcv 函数,根据 TCP_LISTEN 状态判断,调用 tcp_v4_do_rcv -> tcp_rcv_state_process 。
int tcp_v4_rcv(struct sk_buff *skb)
{
...
process:
...
if (sk->sk_state == TCP_LISTEN) {
ret = tcp_v4_do_rcv(sk, skb);
goto put_and_return;
}
...
}
int tcp_v4_do_rcv(struct sock *sk, struct sk_buff *skb)
{
...
if (tcp_rcv_state_process(sk, skb)) {
rsk = sk;
goto reset;
}
return 0;
...
}
tcp_rcv_state_process 函数,同样根据 TCP_LISTEN 判断,如果是 SYN,调用 tcp_v4_conn_request 函数。
int tcp_rcv_state_process(struct sock *sk, struct sk_buff *skb)
{
...
case TCP_LISTEN:
...
if (th->syn) {
if (th->fin)
goto discard;
...
acceptable = icsk->icsk_af_ops->conn_request(sk, skb) >= 0;
...
}
...
}
tcp_v4_conn_request 函数,实际返回调用的 tcp_conn_request 函数。
int tcp_v4_conn_request(struct sock *sk, struct sk_buff *skb)
{
/* Never answer to SYNs send to broadcast or multicast */
if (skb_rtable(skb)->rt_flags & (RTCF_BROADCAST | RTCF_MULTICAST))
goto drop;
return tcp_conn_request(&tcp_request_sock_ops,
&tcp_request_sock_ipv4_ops, sk, skb);
...
}
tcp_conn_request -> tcp_v4_send_synack -> tcp_make_synack,mss 将通过目的路由取到的 MSS 值与通过 setsockopt 设置的值 user_mss 比较,采用较小的一个作为 mss。之后通过 tcp_synack_options 将 mss 赋值给 opts->mss,最终通过 tcp_options_write 写入 options。
int tcp_conn_request(struct request_sock_ops *rsk_ops,
const struct tcp_request_sock_ops *af_ops,
struct sock *sk, struct sk_buff *skb)
{
...
af_ops->send_synack(sk, dst, &fl, req, &foc,
!want_cookie ? TCP_SYNACK_NORMAL :
TCP_SYNACK_COOKIE,
skb);
...
}
static int tcp_v4_send_synack(const struct sock *sk, struct dst_entry *dst,
struct flowi *fl,
struct request_sock *req,
struct tcp_fastopen_cookie *foc,
enum tcp_synack_type synack_type,
struct sk_buff *syn_skb)
{
...
skb = tcp_make_synack(sk, dst, req, foc, synack_type, syn_skb);
...
}
struct sk_buff *tcp_make_synack(const struct sock *sk, struct dst_entry *dst,
struct request_sock *req,
struct tcp_fastopen_cookie *foc,
enum tcp_synack_type synack_type,
struct sk_buff *syn_skb)
{
...
mss = tcp_mss_clamp(tp, dst_metric_advmss(dst));
...
tcp_header_size = tcp_synack_options(sk, req, mss, skb, &opts, md5,
foc, synack_type,
syn_skb) + sizeof(*th);
...
tcp_options_write((__be32 *)(th + 1), NULL, &opts);
...
}
static inline u16 tcp_mss_clamp(const struct tcp_sock *tp, u16 mss)
{
/* We use READ_ONCE() here because socket might not be locked.
* This happens for listeners.
*/
u16 user_mss = READ_ONCE(tp->rx_opt.user_mss);
return (user_mss && user_mss < mss) ? user_mss : mss;
}
static inline u32
dst_metric_advmss(const struct dst_entry *dst)
{
u32 advmss = dst_metric_raw(dst, RTAX_ADVMSS);
if (!advmss)
advmss = dst->ops->default_advmss(dst);
return advmss;
}
实验测试一
通过 setsockopt 选项 TCP_MAXSEG 指定 MSS 值 为 1000,则服务器端 advmss 值为 1000,snd_mss 也为 1000。
# cat tcp_3hs_options_mss_008.pkt
+0 socket(..., SOCK_STREAM, IPPROTO_TCP) = 3
+0 setsockopt(3, SOL_SOCKET, SO_REUSEADDR, [1], 4) = 0
+0 bind(3, ..., ...) = 0
+0 listen(3, 1) = 0
+.1 getsockopt(3, SOL_TCP, TCP_MAXSEG, [536], [4]) = 0
+.1 setsockopt(3, SOL_TCP, TCP_MAXSEG, [1000], 4) = 0
+0 < S 0:0(0) win 10000 <mss 1200>
+0 > S. 0:0(0) ack 1 <...>
+0.01 < . 1:1(0) ack 1 win 10000
+0 accept(3, ..., ...) = 4
+.1 getsockopt(4, SOL_TCP, TCP_MAXSEG, [1000], [4]) = 0
+0 %{print (tcpi_advmss)}%
+0 %{print (tcpi_snd_mss)}%
#
# packetdrill tcp_3hs_options_mss_008.pkt
1000
1000
#
抓包结果可见 SYN MSS 1200(来自于 < mss 1200 自定义值),SYN/ACK MSS 1000(来自于 setsockopt 所设置的 TCP_MAXSEG 1000)。
# tcpdump -i any -nn port 8080
tcpdump: data link type LINUX_SLL2
tcpdump: verbose output suppressed, use -v[v]... for full protocol decode
listening on any, link-type LINUX_SLL2 (Linux cooked v2), snapshot length 262144 bytes
23:50:02.668189 tun0 In IP 192.0.2.1.54997 > 192.168.119.16.8080: Flags [S], seq 0, win 10000, options [mss 1200], length 0
23:50:02.668223 tun0 Out IP 192.168.119.16.8080 > 192.0.2.1.54997: Flags [S.], seq 4217887737, ack 1, win 65000, options [mss 1000], length 0
23:50:02.678345 tun0 In IP 192.0.2.1.54997 > 192.168.119.16.8080: Flags [.], ack 1, win 10000, length 0
23:50:02.802040 ? Out IP 192.168.119.16.8080 > 192.0.2.1.54997: Flags [F.], seq 1, ack 1, win 65000, length 0
23:50:02.802064 ? In IP 192.0.2.1.54997 > 192.168.119.16.8080: Flags [R.], seq 1, ack 1, win 10000, length 0
实验测试二
继续 SYN/ACK MSS 测试,通过修改目的路由 MSS 值 为 1000,从而影响 advmss。
通过 packetdrill pkt 文件中修改 advmss 值为 1000,执行脚本后,tcpdump 捕获结果可以看到 SYN options [mss 1000...] 。
# cat tcp_3hs_options_mss_009.pkt
`ip route change 192.0.2.0/24 dev tun0 advmss 1000`
0 socket(..., SOCK_STREAM, IPPROTO_TCP) = 3
+0 setsockopt(3, SOL_SOCKET, SO_REUSEADDR, [1], 4) = 0
+0 bind(3, ..., ...) = 0
+0 listen(3, 1) = 0
+0 < S 0:0(0) win 10000 <mss 1200>
+0 > S. 0:0(0) ack 1 <...>
+0.01 < . 1:1(0) ack 1 win 10000
+0 accept(3, ..., ...) = 4
#
# packetdrill tcp_3hs_options_mss_009.pkt
#
# tcpdump -i any -nn port 8080
tcpdump: data link type LINUX_SLL2
tcpdump: verbose output suppressed, use -v[v]... for full protocol decode
listening on any, link-type LINUX_SLL2 (Linux cooked v2), snapshot length 262144 bytes
23:50:05.556233 ? In IP 192.0.2.1.60891 > 192.168.125.150.8080: Flags [S], seq 0, win 10000, options [mss 1200], length 0
23:50:05.556260 ? Out IP 192.168.125.150.8080 > 192.0.2.1.60891: Flags [S.], seq 4272403448, ack 1, win 65000, options [mss 1000], length 0
23:50:05.566372 ? In IP 192.0.2.1.60891 > 192.168.125.150.8080: Flags [.], ack 1, win 10000, length 0
23:50:05.566455 ? Out IP 192.168.125.150.8080 > 192.0.2.1.60891: Flags [F.], seq 1, ack 1, win 65000, length 0
23:50:05.566467 ? In IP 192.0.2.1.60891 > 192.168.125.150.8080: Flags [R.], seq 1, ack 1, win 10000, length 0
往期推荐
原文始发于微信公众号(Echo Reply):Wireshark & Packetdrill | TCP 三次握手之 SYN/ACK MSS
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论