IP_TRANSPARENT SYN 未在本地主机上获得 SYN+ACK 响应
IP_TRANSPARENT SYN not getting SYN+ACK response on localhost
我正在编写以下程序(虽然我不认为这是问题所在):
#include <arpa/inet.h>
#include <netinet/ip.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <stdio.h>
#include <stdlib.h>
int main() {
int sock=socket(AF_INET, SOCK_STREAM, 0);
if( sock<0 ) {
perror("socket creation failed");
return 1;
}
int ip_transparent_enabled = 1;
if( setsockopt(sock, IPPROTO_IP, IP_TRANSPARENT, &ip_transparent_enabled, sizeof(ip_transparent_enabled))<0 ) {
perror("Setting IP_TRANSPARENT failed");
return 1;
}
struct sockaddr_in bind_addr = { AF_INET, htons(31337) };
inet_aton("93.184.216.34", &bind_addr.sin_addr); // example.com
if( bind(sock, (const struct sockaddr *)&bind_addr, sizeof(bind_addr))<0 ) {
perror("bind failed");
return 1;
}
struct sockaddr_in dest = { AF_INET, htons(7007) };
inet_aton("127.0.0.1", &dest.sin_addr);
if( connect(sock, (const struct sockaddr *)&dest, sizeof(dest))<0 ) {
perror("Connect failed");
return 1;
}
}
端口 7007 运行正在连接一个回显服务器,但这并不重要,因为那里的程序永远不会接收到连接。
当我 运行 tcpdump 时,我看到 SYN 是用正确的(编造的)源地址发送的,但是 SYN+ACK 既没有在环回上也没有在 eth0 上发出:
$ sudo tcpdump -i any port 31337
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on any, link-type LINUX_SLL (Linux cooked), capture size 262144 bytes
22:22:41.475942 IP 93.184.216.34.31337 > localhost.afs3-bos: Flags [S], seq 2953286612, win 65495, options [mss 65495,sackOK,TS val 443673031 ecr 0,nop,wscale 7], length 0
22:22:42.478172 IP 93.184.216.34.31337 > localhost.afs3-bos: Flags [S], seq 2953286612, win 65495, options [mss 65495,sackOK,TS val 443674033 ecr 0,nop,wscale 7], length 0
22:22:44.494174 IP 93.184.216.34.31337 > localhost.afs3-bos: Flags [S], seq 2953286612, win 65495, options [mss 65495,sackOK,TS val 443676049 ecr 0,nop,wscale 7], length 0
22:22:48.590423 IP 93.184.216.34.31337 > localhost.afs3-bos: Flags [S], seq 2953286612, win 65495, options [mss 65495,sackOK,TS val 443680145 ecr 0,nop,wscale 7], length 0
如果我注释掉 bind
,那么一切都会按预期进行。似乎没有相关的防火墙端口,rp_filter
在环回时设置为 0。
为什么连接不被接受?这是我代码中的错误,还是与此配置有关?
您正在尝试创建从某个外部地址 (93.184.216.34) 到 127.0.0.1 的 TCP 连接。因此,必须使用源 127.0.0.1 和外部地址作为目标来创建 SYN+ACK。只是,没有使用环回接口(127.0.0.1)到该外部地址的路由,因此无法发送 SYN+ACK。
丢失的SYN+ACK应该有以下信息:
127.0.0.1:7007 -> 93.184.216.34:31337
在遍历路由 table 时,目标 IP 将匹配默认路由,并会安排在 eth0
出去(如预期)。
但是,eth0
启用了(与系统中所有其他接口一样)Martian packet 保护。由于源地址是127.0.0.1
,包永远不会出去,也不会被tcpdump
.
报告
一种解决方案是关闭 eth0
的火星数据包保护:
# echo 0 > /proc/sys/net/ipv4/conf/eth0/route_localnet
但是,这会禁用非常有用的保护。一个更好的解决方案是将此流量定向到环回接口,以使用源路由开始:
# ip rule add from 127.0.0.1 lookup 100
# ip route add local default dev lo table 100
有了这个,就可以使用外部 IP 地址和端口在透明代理和本地服务之间建立实际连接。
我正在编写以下程序(虽然我不认为这是问题所在):
#include <arpa/inet.h>
#include <netinet/ip.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <stdio.h>
#include <stdlib.h>
int main() {
int sock=socket(AF_INET, SOCK_STREAM, 0);
if( sock<0 ) {
perror("socket creation failed");
return 1;
}
int ip_transparent_enabled = 1;
if( setsockopt(sock, IPPROTO_IP, IP_TRANSPARENT, &ip_transparent_enabled, sizeof(ip_transparent_enabled))<0 ) {
perror("Setting IP_TRANSPARENT failed");
return 1;
}
struct sockaddr_in bind_addr = { AF_INET, htons(31337) };
inet_aton("93.184.216.34", &bind_addr.sin_addr); // example.com
if( bind(sock, (const struct sockaddr *)&bind_addr, sizeof(bind_addr))<0 ) {
perror("bind failed");
return 1;
}
struct sockaddr_in dest = { AF_INET, htons(7007) };
inet_aton("127.0.0.1", &dest.sin_addr);
if( connect(sock, (const struct sockaddr *)&dest, sizeof(dest))<0 ) {
perror("Connect failed");
return 1;
}
}
端口 7007 运行正在连接一个回显服务器,但这并不重要,因为那里的程序永远不会接收到连接。
当我 运行 tcpdump 时,我看到 SYN 是用正确的(编造的)源地址发送的,但是 SYN+ACK 既没有在环回上也没有在 eth0 上发出:
$ sudo tcpdump -i any port 31337
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on any, link-type LINUX_SLL (Linux cooked), capture size 262144 bytes
22:22:41.475942 IP 93.184.216.34.31337 > localhost.afs3-bos: Flags [S], seq 2953286612, win 65495, options [mss 65495,sackOK,TS val 443673031 ecr 0,nop,wscale 7], length 0
22:22:42.478172 IP 93.184.216.34.31337 > localhost.afs3-bos: Flags [S], seq 2953286612, win 65495, options [mss 65495,sackOK,TS val 443674033 ecr 0,nop,wscale 7], length 0
22:22:44.494174 IP 93.184.216.34.31337 > localhost.afs3-bos: Flags [S], seq 2953286612, win 65495, options [mss 65495,sackOK,TS val 443676049 ecr 0,nop,wscale 7], length 0
22:22:48.590423 IP 93.184.216.34.31337 > localhost.afs3-bos: Flags [S], seq 2953286612, win 65495, options [mss 65495,sackOK,TS val 443680145 ecr 0,nop,wscale 7], length 0
如果我注释掉 bind
,那么一切都会按预期进行。似乎没有相关的防火墙端口,rp_filter
在环回时设置为 0。
为什么连接不被接受?这是我代码中的错误,还是与此配置有关?
您正在尝试创建从某个外部地址 (93.184.216.34) 到 127.0.0.1 的 TCP 连接。因此,必须使用源 127.0.0.1 和外部地址作为目标来创建 SYN+ACK。只是,没有使用环回接口(127.0.0.1)到该外部地址的路由,因此无法发送 SYN+ACK。
丢失的SYN+ACK应该有以下信息:
127.0.0.1:7007 -> 93.184.216.34:31337
在遍历路由 table 时,目标 IP 将匹配默认路由,并会安排在 eth0
出去(如预期)。
但是,eth0
启用了(与系统中所有其他接口一样)Martian packet 保护。由于源地址是127.0.0.1
,包永远不会出去,也不会被tcpdump
.
一种解决方案是关闭 eth0
的火星数据包保护:
# echo 0 > /proc/sys/net/ipv4/conf/eth0/route_localnet
但是,这会禁用非常有用的保护。一个更好的解决方案是将此流量定向到环回接口,以使用源路由开始:
# ip rule add from 127.0.0.1 lookup 100
# ip route add local default dev lo table 100
有了这个,就可以使用外部 IP 地址和端口在透明代理和本地服务之间建立实际连接。