透明代理的實作目前Linuxn內核提供兩種實作的方式:NAT和TPROXY,Nat 方式其實就是內內核通過地址轉換實作的;而 TPROXY 是內核通過對設定的資料包打標記,然后通過策略路由將打標記的資料包重定向到本地監聽行程上,此次我們借助iptables的nat表的規則對資料包進行重定向,具體配置及步驟如下,
試驗環境
將試驗環境的配置如下:
| 名稱 | IP地址 | 網關設定 |
| 客戶端 | 192.168.200.184 | 192.168.200.111 |
| 代理服務器 | 192.168.200.111 | 192.168.200.1(內網真實網關IP) |
| 服務器 | 172.16.9.66 |
環境網路結構圖如下:

試驗步驟
1、需要添加iptables規則,將經過代理服務器的資料包重定向到本地代理監聽行程上去;具體添加規則命令如下:
1、在nat表上新建名為MY_TCP自定義鏈
iptables -t nat -N MY_TCP2、將MY_TCP加入到PREROUTING鏈后
iptables -t nat -A PREROUTING -d 172.16.9.66 -j MY_TCP3、將特定資料包轉到6666埠
iptables -t nat -A MY_TCP -p tcp --dport 12345 -j REDIRECT --to-ports 2223
2、試驗程序的客戶端跟服務器用TCP工具創建一個客戶端跟服務端,透明代理端需寫簡單的服務端代碼,代碼如下:
#include <stdio.h>
#include <string.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <linux/netfilter_ipv4.h>
#define PORT 2223 //埠號
#define BACKLOG 5 //最大監聽數
int main()
{
int iSocketFD = 0; //socket句柄
int iRecvLen = 0; //接收成功后的回傳值
int new_fd = 0; //建立連接后的句柄
char buf[4096] = {0}; //
int n = 0;
int ret = 0;
struct sockaddr_in stLocalAddr = {0}; //本地地址資訊結構圖,下面有具體的屬性賦值
struct sockaddr_in stRemoteAddr = {0}; //對方地址資訊
socklen_t socklen = 0;
iSocketFD = socket(AF_INET, SOCK_STREAM, 0); //建立socket
if(0 > iSocketFD)
{
printf("創建socket失敗!\n");
return 0;
}
stLocalAddr.sin_family = AF_INET; /*該屬性表示接收本機或其他機器傳輸*/
stLocalAddr.sin_port = htons(PORT); /*埠號*/
stLocalAddr.sin_addr.s_addr=htonl(INADDR_ANY); /*IP,括號內容表示本機IP*/
//系結地址結構體和socket
if(0 > bind(iSocketFD, (void *)&stLocalAddr, sizeof(stLocalAddr)))
{
printf("系結失敗!\n");
return 0;
}
//開啟監聽 ,第二個引數是最大監聽數
if(0 > listen(iSocketFD, BACKLOG))
{
printf("監聽失敗!\n");
return 0;
}
printf("iSocketFD: %d\n", iSocketFD);
//在這里阻塞知道接收到訊息,引數分別是socket句柄,接收到的地址資訊以及大小
while(1)
{
new_fd = accept(iSocketFD, (void *)&stRemoteAddr, &socklen);
printf("new_fd: %d\n", new_fd);
if(0 > new_fd)
{
printf("接收失敗!\n");
return 0;
}else{
printf("接收成功!\n");
n = sizeof(struct sockaddr_in);
ret = getsockopt(new_fd, SOL_IP, SO_ORIGINAL_DST, &stRemoteAddr, &n);
if(0 != ret)
{
printf ("error getting original destination address.\n");
close (new_fd);
return -1;
}
stRemoteAddr.sin_family = AF_INET;
printf("original destination address %u:%hu\n", ntohl(stRemoteAddr.sin_addr.s_addr), ntohs(stRemoteAddr.sin_port));
//發送內容,引數分別是連接句柄,內容,大小,其他資訊(設為0即可)
recv(new_fd, buf, sizeof(buf), 0);
printf("從客戶端上接收到的資訊是:%s\n", buf);
//send(new_fd, "這是代理服務器接收成功后發回的資訊!", sizeof("這是代理服務器接收成功后發回的資訊!"), 0);
/* 連接服務器*/
int iSockClientFD = 0;
iSockClientFD = socket(AF_INET, SOCK_STREAM, 0);
if(0 > iSockClientFD)
{
printf("代理向服務器建立連接失敗!\n");
return 0;
}
if(0 > connect(iSockClientFD, (void *)&stRemoteAddr, sizeof(stRemoteAddr)))
{
printf("代理向服務器建立連接失敗!\n");
return 0;
}
send(iSockClientFD, buf, sizeof(buf), 0);//向服務器發送訊息
recv(iSockClientFD, buf, sizeof(buf), 0);//接收來自服務器的訊息
printf("從服務器端收到的訊息:%s\n", buf);//列印接收到的來自服務器的訊息
send(new_fd, buf, sizeof(buf), 0);//向客戶端發送訊息
sleep(5);
}
}
close(new_fd);
close(iSocketFD);
return 0;
}
使用結果截圖如下:
1、代理服務器配置規則如下:

2、客戶端、服務器試驗截圖如下:

3、代理服務器的代理監聽程式列印如下:

轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/317865.html
標籤:其他
