本站首页    管理页面    写新日志    退出


«April 2019»
123456
78910111213
14151617181920
21222324252627
282930


公告

戒除浮躁,读好书,交益友


我的分类(专题)

日志更新

最新评论

留言板

链接

Blog信息
blog名称:邢红瑞的blog
日志总数:523
评论数量:1142
留言数量:0
访问次数:9244090
建立时间: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,感觉很诡异。


阅读全文(5340) | 回复(0) | 编辑 | 精华
 



发表评论:
昵称:
密码:
主页:
标题:
验证码:  (不区分大小写,请仔细填写,输错需重写评论内容!)



站点首页 | 联系我们 | 博客注册 | 博客登陆

Sponsored By W3CHINA
W3CHINA Blog 0.8 Processed in 0.047 second(s), page refreshed 144256060 times.
《全国人大常委会关于维护互联网安全的决定》  《计算机信息网络国际联网安全保护管理办法》
苏ICP备05006046号