接着昨天的继续看,说完收包再来说发包! 发包逻辑主要涉及到:tcp三次握手时:发送syn--TCP_SYN_SENT状态、syn_ack---TCP_NEW_SYN_RECV状态、TCP_LISTEN 、TCP_ESTABLISHED 等状态时发包
首先看下 作为服务端时, TCP_LISTEN 、TCP_NEW_SYN_RECV 状态时发包处理:
一、服务端收到syn 报文时,tcp 会回复syn_ack并设置为TCP_NEW_SYN_RECV 状态,那么此时发送报文和路由有什么关系呢?
tcp_v4_conn_request 是回复syn_ack时处理函数;来看看主要涉及到路由的路由部分
1、首先检查input_skb报文路由的特性,丢弃broadcast or multicast
2、收到第一步SYN的时候只是建立的连接控制块request_sock
3、生成出口发包路由等
4、build tcp pkt 然后send syn_ack pkt
此时对于Tproxy逻辑来说,需要修改 tcp_make_synack 以及ip_send_pkt;一般都会根据ireq->no_srccheck 标志来检查是否使用tproxy 逻辑发送报文;
如果使用tproxy 发送报文,此时需要make_pkt_send 根据自己需要发送,同时也是绑定特定接口发出dev_queue_xmit,所以此时生成的route 无用;
二、服务端收到ack报文时,从TCP_NEW_SYN_RECV变为TCP_ESTABLISHED,那么此时发送报文和路由有什么关系呢?
三次握手的第二阶段,服务器发送synack后,会进入TCP_NEW_SYN_RECV状态,并插入ehash中。 收到握手最后一个ack后,会找到TCP_NEW_SYN_RECV状态的req,然后创建一个新的sock进入TCP_SYN_RECV状态,最终进入TCP_ESTABLISHED状态. 并放入accept队列通知select/epoll
在tcp_child_process中调用tcp_rcv_state_process来处理TCP_SYN_RECV状态的child sock。并进入TCP_ESTABLISHED状态
tcp_rcv_state_process 在此时条件下主要逻辑为: TCP_SYN_RECV------>TCP_ESTABLISHED 唤醒epfd 初始化tcp 相关参数 同时检查是否有需要发送数据
检查发送报文是,tcp状态为TCP_ESTABLISHED
TCP TCP_ESTABLISHED 状态时 发报文和路由缓存的关系
三、TCP 为TCP_ESTABLISHED 状态时 发报文和路由缓存的关系
此时tcp 发送报文会调用ip_queue_xmit 发送ip报文,ip层会涉及到路由:
最后根据路由调用:ip_output()或者ip_mc_output() 封装发送ip层报文
所以对于上述场景: 在tproxy 的使用场景下,完全是可以不需要rt,因为此时发包时肯定指定接口了!!wan口和lan口是一对接口,lan口进wan出,wan口进lan口出-----
再来看看tproxy 客户端场景下的分析:
一:客户端主动发包建立三次握手,发出syn ---->TCP_SYN_SENT
connect 系统调用主要函数 tcp_v4_connect为:
但是对于主动发包时:此时使用的socket 只是随便create的一个socket,由于其src_ip dst_ip 是外部client 以及服务器ip,所以发包时要根据原始session 填充build_tcp_ip_pkt;
此时肯定不能走原始的tcp_connect逻辑需要重新修改connect 逻辑:
sk=socket();
bind(clinet_ip,clinet_port)
set_opt(transparent, );
connect(server_ip server port)
所以sys_connect:逻辑中需要 根据client_ip_port server_ip_port 找到 原始 established_socket;
然后copy 原始src_mac dst_mac input_ifindex;找出output_ifdex;设置 bind==> inet->sk.sk_bound_dev_if = ifindex;
原始的connect 会调用ip_route_slow 查找路由,此时bind了接口,所以路由出接口就知道了,
相当于此时可以不需要路由rt;
接着inet_hash_connect insert 到established散列表 方便收到ack 的时候查找,目前只有established hash 和listen hash
封装tcp 后调用ip_queue_xmit 发送封装ip 层报文然后发送------
可以此时也可以不需要路由;因为都已经bind ifindex 了 但是发包时sk的src ip dst ip port等要设置正确
二:客户端收到syn_ack 发出ack时, 从TCP_SYN_SENT---->TCP_ESTABLISHED
也就是tcp_rcv_state_process的逻辑
可知 同上述一样 可以不需要路由rt
所以:
目前认为在tproxy下: wan口和lan口是一对,缺少路由无所谓!! 去掉路由逻辑应该也非常方便
只需要判断sk 是否为transparent;
bool tproxy_sk_is_transparent(struct sock *sk) { switch (sk->sk_state) { case TCP_TIME_WAIT: if (inet_twsk(sk)->tw_transparent) return true; break; case TCP_NEW_SYN_RECV: if (inet_rsk(inet_reqsk(sk))->no_srccheck) return true; break; default: if (inet_sk(sk)->transparent) return true; } return false; }