1. IP_TRANSPARENT
[1]socket设置该选项后,可以处理发往非本机的数据包。
[2]使用流程:
配置防火墙和路由:
iptables -t mangle -A PREROUTING ! -d 10.0.110.250 -p tcp -j TPROXY --on-port 10000 --on-ip 0.0.0.0 --tproxy-mark 0x1/0x1
ip rule add fwmark 1 lookup 100
ip route add local 0.0.0.0/0 dev lo table 100
代码:
//创建监听socket
int option = 1;
struct sockaddr_in addr;
addr.sin_addr.s_addr = 0;
addr.sin_port = 10000;
addr.sin_family = PF_INET;
bind(fd, (struct sockaddr*)&addr, sizeof(struct sockaddr_in));
setsockopt(fd, SOL_IP, IP_TRANSPARENT, &option, sizeof(option));
setsockopt(fd, IPPROTO_IP, IP_RECVORIGDSTADDR, &option, sizeof(option)); // udp socket需要设置
//tcp获取客户端连接的真实服务器地址
struct sockaddr_in client_addr, server_addr;
socklen_t addrlen = sizeof(struct sockaddr_in);
new_fd = accept(fd, (struct sockaddr *)&client_addr, &addrlen);
getsockname(fd, (struct sockaddr *)&server_addr, &addrlen);
//udp获取客户端连接的真实服务器地址
int found;
char buffer[1024];
char cntrlbuf[64];
struct iovec iov[1];
struct msghdr msg;
struct cmsghdr *cmsg;
struct sockaddr_in client_addr, server_addr;
msg.msg_name = &client_addr;
msg.msg_namelen = sizeof(struct sockaddr_in);
msg.msg_control = cntrlbuf;
msg.msg_controllen = sizeof(cntrlbuf);
iov[0].iov_base = buffer;
iov[0].iov_len = sizeof(buffer);
msg.msg_iov = iov;
msg.msg_iovlen = 1;
ret = recvmsg(fd, &msg, 0);
if(ret >= 0){
found = 0;
for(cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)){
if(cmsg->cmsg_level == SOL_IP && cmsg->cmsg_type == IP_RECVORIGDSTADDR){
memcpy(&server_addr, CMSG_DATA(cmsg), sizeof(struct sockaddr_in));
server_addr.sin_family = AF_INET;
found = 1;
}
}
}
2.SO_REUSEADDR
[1]这个选项表示复用地址,表示多个socket可以绑定到同一个地址。
[2]TCP协议时,设置该选项后,可以使用相同的地址去连接不同的服务器地址。但是不可以去连接相同的服务器地址,除非本地的当前连接处于TIME_WAIT状态。
[3]TCP协议时,如果一个地址已经被bind,则另一个socket不可以再次listen。如果一个地址已经被socket执行listen,则另一个socket不可以再次bind。
[4]UDP协议时,设置该选项后,可以多个socket绑定同一个地址去接收数据,但是只有最后一个socket可以收到数据(需要继续分析代码)。
需要注意的是socket的地址格式是ip:port,只有ip和port都相等时才存在复用,有一个不等则不存在复用这一说。