« | August 2025 | » | 日 | 一 | 二 | 三 | 四 | 五 | 六 | | | | | | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | | | | | | | |
| 公告 |
戒除浮躁,读好书,交益友 |
Blog信息 |
blog名称:邢红瑞的blog 日志总数:523 评论数量:1142 留言数量:0 访问次数:9692448 建立时间:2004年12月20日 |

| |
[linux kernel]iptables nat的REDIRECT 原创空间, 文章收藏, 软件技术
邢红瑞 发表于 2010/3/29 17:30:59 |
很久以前做网站的时候,OS多使用FreeBSD,当使用pf和squid作透明代理时,没有发现什么特殊之处。后来在了linux下面开发VPN的时候,服务器在接收连接时,不知道客户端发出连接的目的IP地址和端口。无论是TDI还是linux的IPTABLES,TDI暂且不说,linux的DNAT中的REDIRECT改变了目的IP地址和端口,否则就不算DNAT了。对于squid处理是一致的,看squid代码就知道原因。squid 2.6 的client_side.c#elif LINUX_NETFILTERstatic intclientNatLookup(ConnStateData * conn){ socklen_t sock_sz = sizeof(conn->me); struct in_addr orig_addr = conn->me.sin_addr; static time_t last_reported = 0; /* If the call fails the address structure will be unchanged */ if (getsockopt(conn->fd, SOL_IP, SO_ORIGINAL_DST, &conn->me, &sock_sz) != 0) { if (squid_curtime - last_reported > 60) { debug(50, 1) ("clientNatLookup: NF getsockopt(SO_ORIGINAL_DST) failed: %s\n", xstrerror()); last_reported = squid_curtime; } return -1; } debug(33, 5) ("clientNatLookup: addr = %s", inet_ntoa(conn->me.sin_addr)); if (orig_addr.s_addr != conn->me.sin_addr.s_addr) return 0; else return -1;}#elif PF_TRANSPARENTstatic intclientNatLookup(ConnStateData * conn){ struct pfioc_natlook nl; static int pffd = -1; static time_t last_reported = 0; if (pffd < 0) { pffd = open("/dev/pf", O_RDONLY); if (pffd >= 0) commSetCloseOnExec(pffd); } if (pffd < 0) { debug(50, 1) ("clientNatLookup: PF open failed: %s\n", xstrerror()); return -1; } memset(&nl, 0, sizeof(struct pfioc_natlook)); nl.saddr.v4.s_addr = conn->peer.sin_addr.s_addr; nl.sport = conn->peer.sin_port; nl.daddr.v4.s_addr = conn->me.sin_addr.s_addr; nl.dport = conn->me.sin_port; nl.af = AF_INET; nl.proto = IPPROTO_TCP; nl.direction = PF_OUT; if (ioctl(pffd, DIOCNATLOOK, &nl)) { if (errno != ENOENT) { if (squid_curtime - last_reported > 60) { debug(50, 1) ("clientNatLookup: PF lookup failed: ioctl(DIOCNATLOOK)\n"); last_reported = squid_curtime; } close(pffd); pffd = -1; } return -1; } else { int natted = conn->me.sin_addr.s_addr != nl.rdaddr.v4.s_addr; conn->me.sin_port = nl.rdport; conn->me.sin_addr = nl.rdaddr.v4; if (natted) return 0; else return -1; }}BSD和linux 处理是不一致的。找到一段代码#include <stdio.h>#include <string.h>#include <stdlib.h>
#include <unistd.h>#include <sys/types.h>#include <sys/socket.h>#include <sys/ioctl.h>#include <netinet/in.h>#include <arpa/inet.h>#include <netdb.h>#include <errno.h>#include <dlfcn.h>#include <linux/netfilter_ipv4.h>
#define SD_BOTH 2#define SOCKET int#define INVALID_SOCKET (SOCKET)(~0)#define SOCKET_ERROR (-1)
int main(void){SOCKET listen_sock, new_sock;struct sockaddr_in client;struct sockaddr_in me;struct sockaddr_in peer;
unsigned long sockmode = 0;socklen_t SLen = sizeof(client);
if ((listen_sock = socket(AF_INET, SOCK_STREAM, 0))== INVALID_SOCKET) {exit(1);}
/* Local address and port */me.sin_family = AF_INET;me.sin_port = htons(4321);inet_aton("127.0.0.1", &me.sin_addr);
if (ioctl(listen_sock, FIONBIO, &sockmode) == SOCKET_ERROR) {close(listen_sock);exit(1);}
if (bind(listen_sock, (struct sockaddr *)&me, sizeof(me)) == SOCKET_ERROR) {close(listen_sock);exit(1);}
if (listen(listen_sock, SOMAXCONN) == SOCKET_ERROR) {shutdown(listen_sock, SD_BOTH);close(listen_sock);exit(1);}
while ((new_sock = accept(listen_sock, (struct sockaddr *)&client, &SLen))!= INVALID_SOCKET){SLen = sizeof(peer);memset(&peer, 0, SLen);getsockopt(new_sock, SOL_IP, SO_ORIGINAL_DST, &peer, &SLen);printf( "Client: %s:%hu\n", inet_ntoa(peer.sin_addr), ntohs(peer.sin_port));
if ( shutdown(new_sock, SD_BOTH) == SOCKET_ERROR ||close(new_sock) == SOCKET_ERROR ){shutdown(listen_sock, SD_BOTH);close(listen_sock);exit(1);}}if(new_sock == INVALID_SOCKET) {shutdown(listen_sock, SD_BOTH);close(listen_sock);exit(1);}
if (shutdown(listen_sock, SD_BOTH) == SOCKET_ERROR ||close(listen_sock) == SOCKET_ERROR){exit(1);}
exit(0);}linux中输入 iptables -t nat -I OUTPUT -p tcp -j REDIRECT --to 4321iptables -t nat -I PREROUTING -p tcp -i eth0 --dport pop3 -j REDIRECT --to 4321然后输入 telnet 后面是随意的ip地址就行。java中的得不到socket的真实fd,感觉很诡异。 |
|
|