当前位置:网站首页>tcp transparent proxy (IP_TRANSPARENT)
tcp transparent proxy (IP_TRANSPARENT)
2022-08-02 14:12:00 【Soonyang Zhang】
Introduction
tcp透明代理,只是测试demo。
With the support of linux kernal, a proxy on a router can intercept tcp traffic and sends the packet to destination by enble IP_TRANSPARENT. And the addresses is not changed.
This demo owns much thanks to go-tproxy[1].
# 1.0 2.0 3.0
# h1----s1----h2------h3-------h4
The demo is tested on mininet. sproof_tcp works on h2 and listens on port 2233. echo client works on h1 (10.0.1.1) and sends packets to h3 (10.0.2.2:3345)。When the packet arrives h2, the packet with dest (10.0.2.2:3345) is diverted on 2233 port and is intercepted by sproof_tcp. sproof_tcp start a new connection and bind on a nonlocal address(10.0.1.1:port) and sends packet to h3. The detail can be found on bind_fake_addr fun.The src ip of packet is still 10.0.1.1 when packet arrives on h3.
The packet diverting on h2 is enabled by the configuration on iptable. With the following configuration, only packet with dest (10.0.2.2:3345) ("-d 10.0.2.2-d 10.0.2.2 --dport 3345") will be diverted.
iptables -t mangle -N DIVERT
iptables -t mangle -A PREROUTING -p tcp -m socket -j DIVERT
iptables -t mangle -A DIVERT -j MARK --set-mark 1
iptables -t mangle -A DIVERT -j ACCEPTT
ip rule add fwmark 1 lookup 100
ip route add local 0.0.0.0/0 dev lo table 100
iptables -t mangle -A PREROUTING -p tcp -d 10.0.2.2 --dport 3345 -j TPROXY --tproxy-mark 0x1/0x1 --on-port 2233
Code
sproof_tcp.cc
/* Author: zsy Create: 2021/03/21 Ref: https://github.com/LiamHaworth/go-tproxy */
#include <iostream>
#include <string.h>
#include <stdint.h>
#include <signal.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/epoll.h>
#include <errno.h>
#include <fcntl.h>
#include <netinet/in.h> //for sockaddr_in
#include <arpa/inet.h> //inet_addr
#include <linux/netfilter_ipv4.h>
#include <netdb.h>
#include <map>
#define LOC __FILE__<<__LINE__<<" "
namespace tcp{
int setnonblocking(int fd);
void epoll_ctl_add(int epfd, int fd,uint32_t events);
void epoll_ctl_mod(int epfd, int fd,uint32_t events);
void epoll_ctl_del(int epfd, int fd);
int create_listen_fd(const char *ip,uint16_t port,int backlog,bool transprent);
int setnonblocking(int fd)
{
if (-1==fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) | O_NONBLOCK)){
return -1;
}
return 0;
}
void epoll_ctl_add(int epfd, int fd,uint32_t events)
{
struct epoll_event ev;
ev.events = events|EPOLLERR|EPOLLHUP;
ev.data.fd=fd;
if (epoll_ctl(epfd, EPOLL_CTL_ADD, fd, &ev)==-1) {
std::cout<<"epoll_ctl_add: "<<strerror(errno)<<std::endl;
exit(1);
}
}
void epoll_ctl_mod(int epfd, int fd,uint32_t events)
{
struct epoll_event ev;
ev.events = events|EPOLLERR|EPOLLHUP;
ev.data.fd=fd;
if (epoll_ctl(epfd, EPOLL_CTL_MOD, fd, &ev)==-1) {
std::cout<<"epoll_ctl_mod: "<<strerror(errno)<<std::endl;
exit(1);
}
}
void epoll_ctl_del(int epfd, int fd){
struct epoll_event ev;
memset(&ev, 0, sizeof(ev));
if (epoll_ctl(epfd, EPOLL_CTL_DEL, fd, &ev)==-1) {
std::cout<<"epoll_ctl_del: "<<strerror(errno)<<std::endl;
exit(1);
}
}
int create_listen_fd(const char *ip,uint16_t port,int backlog,bool transparent){
int fd=-1;
int yes=1;
struct sockaddr_in servaddr;
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr =inet_addr(ip);
servaddr.sin_port = htons(port);
size_t addr_size = sizeof(struct sockaddr_in);
fd=socket(AF_INET, SOCK_STREAM, 0);
if(fd<0){
return fd;
}
if(setsockopt(fd,SOL_SOCKET,SO_REUSEADDR,&yes,sizeof(int))!=0){
close(fd);
fd=-1;
return fd;
}
if(setsockopt(fd,SOL_SOCKET, SO_REUSEPORT,&yes ,sizeof(int))!=0){
close(fd);
fd=-1;
return fd;
}
if(bind(fd, (struct sockaddr *)&servaddr, addr_size)<0){
std::cout<<LOC<<strerror(errno)<<std::endl;
close(fd);
fd=-1;
return fd;
}
if(transparent&&setsockopt(fd, SOL_IP, IP_TRANSPARENT, &yes, sizeof(int))!=0){
close(fd);
fd=-1;
std::cout<<"IP_TRANSPARENT failed"<<strerror(errno)<<std::endl;
return fd;
}
if(-1==listen(fd,backlog)){
close(fd);
fd=-1;
return fd;
}
return fd;
}
int bind_fake_addr(struct sockaddr *addr){
int fd=-1;
int yes=1;
fd=socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if(fd<0){
std::cout<<LOC<<strerror(errno)<<std::endl;
return fd;
}
if(setsockopt(fd,SOL_SOCKET,SO_REUSEADDR,&yes,sizeof(int))!=0){
std::cout<<LOC<<strerror(errno)<<std::endl;
close(fd);
fd=-1;
return fd;
}
if(setsockopt(fd, SOL_IP, IP_TRANSPARENT, &yes, sizeof(int))!=0){
std::cout<<LOC<<strerror(errno)<<std::endl;
close(fd);
fd=-1;
return fd;
}
//struct sockaddr_in *addr_in=(struct sockaddr_in*)addr;
//std::cout<<LOC<<inet_ntoa(addr_in->sin_addr)<<":"<<ntohs(addr_in->sin_port)<<std::endl;
if(bind(fd, (struct sockaddr *)addr,sizeof(struct sockaddr_in))<0){
std::cout<<LOC<<errno<<strerror(errno)<<std::endl;
close(fd);
fd=-1;
return fd;
}
return fd;
}
}
const char *bind_ip=(const char*)"0.0.0.0";
const uint16_t bind_port=2233;
const int kMaxBacklog=128;
const int kMaxEvents=64;
const int kNumAgents=2;
const int kEpochs=4;
const int kBufferSize=1500;
const int FD_CONNECTING=0x00;
const int FD_CONNECTED=0x01;
struct FdWrap{
FdWrap(int fd):fd(fd){
}
int fd=-1;
FdWrap *peer=nullptr;
int fd_status=FD_CONNECTING;
std::string wb;
bool operator <(FdWrap const &other) const{
return fd<other.fd;
}
};
static volatile bool g_running=true;
using namespace std;
using namespace tcp;
void signal_exit_handler(int sig){
g_running=false;
}
int main(int argc, char *argv[]){
signal(SIGTERM, signal_exit_handler);
signal(SIGINT, signal_exit_handler);
signal(SIGTSTP, signal_exit_handler);
char buffer[kBufferSize];
int epfd,nfds;
struct epoll_event events[kMaxEvents];
std::map<int,FdWrap*> fd_db;
int listen_fd=create_listen_fd(bind_ip,bind_port,kMaxBacklog,true);
if(listen_fd<0){
return -1;
}
epfd = epoll_create1(0);
if(-1==epfd){
close(listen_fd);
return -1;
}
std::cout<<bind_ip<<":"<<bind_port<<std::endl;
setnonblocking(listen_fd);
epoll_ctl_add(epfd,listen_fd,EPOLLIN|EPOLLET);
while(g_running){
nfds= epoll_wait (epfd, events, kMaxEvents, 0);
for(int i=0;i<nfds;i++){
int fd=events[i].data.fd;
if((events[i].events & EPOLLERR) ||
(events[i].events & EPOLLHUP)){
if(fd!=listen_fd){
FdWrap *self=nullptr;
FdWrap *peer=nullptr;
auto it=fd_db.find(fd);
if (it!=fd_db.end()){
self=it->second;
peer=self->peer;
fd_db.erase(it);
}
int fd2=-1;
if (peer){
fd2=peer->fd;
}
if (fd2>0){
fd_db.erase(fd2);
epoll_ctl_del(epfd,fd2);
close(fd2);
}
if(self){
delete self;
}
if(peer){
delete peer;
}
epoll_ctl_del(epfd,fd);
close(fd);
}
continue;
}
if(events[i].events &EPOLLIN){
if (listen_fd==fd){
int left=-1;
sockaddr_storage addr_storage_src;
sockaddr_storage addr_storage_dst;
socklen_t addr_len = sizeof(sockaddr_storage);
while((left=accept(listen_fd,(sockaddr*)&addr_storage_src,&addr_len))>=0){
setnonblocking(left);
bool success=true;
getpeername(left,(sockaddr*)&addr_storage_src,&addr_len);
getsockname(left,(sockaddr*)&addr_storage_dst,&addr_len);
int right=bind_fake_addr((sockaddr*)&addr_storage_src);
std::cout<<LOC<<right<<std::endl;
if(right<0){
success=false;
}else{
// for asyn connect
setnonblocking(right);
}
if(success){
epoll_ctl_add(epfd,right,EPOLLET|EPOLLIN|EPOLLOUT);
if(connect(right,(struct sockaddr *)&addr_storage_dst,addr_len) == -1&& errno != EINPROGRESS){
//connect doesn't work, are we running out of available ports ? if yes, destruct the socket
if(errno == EAGAIN){
epoll_ctl_del(epfd,right);
success=false;
}
}
}else{
success=false;
}
if(success){
FdWrap *client=new FdWrap(left);
FdWrap *server=new FdWrap(right);
client->peer=server;
server->peer=client;
client->fd_status=FD_CONNECTED;
server->fd_status=FD_CONNECTING;
fd_db.insert(std::make_pair(left,client));
fd_db.insert(std::make_pair(right,server));
epoll_ctl_add(epfd,left,EPOLLET|EPOLLIN);
}else{
if(right>0){
close(right);
}
close(left);
}
}
}else{
auto it=fd_db.find(fd);
FdWrap *self=nullptr;
FdWrap *peer=nullptr;
if(it!=fd_db.end()){
self=it->second;
peer=self->peer;
}
while(true){
int off=0;
int n=read(fd,buffer+off,kBufferSize-off);
if(-1==n){
if(EAGAIN==errno){
//no data in read buffer
break;
}else{
epoll_ctl_del(epfd,fd);
close(fd);
if (self){
fd_db.erase(it);}
if(peer&&peer->fd){
epoll_ctl_del(epfd,peer->fd);
close(peer->fd);
fd_db.erase(peer->fd);
}
if(self){
delete self;
}
if(peer){
delete peer;
}
break;
}
}else if(0==n){
std::cout<<fd<<" conection closed"<<std::endl;
epoll_ctl_del(epfd,fd);
close(fd);
fd_db.erase(it);
if(peer&&peer->fd){
epoll_ctl_del(epfd,peer->fd);
close(peer->fd);
fd_db.erase(peer->fd);
}
if(self){
delete self;
}
if(peer){
delete peer;
}
break;
}else{
off+=n;
if(peer&&peer->fd>0){
if(peer->fd_status==FD_CONNECTING){
int old=peer->wb.size();
peer->wb.resize(old+off);
memcpy(&peer->wb[old],buffer,off);
}else{
write(peer->fd,buffer,off);
}
}else{
std::cout<<LOC<<"where is the peer"<<std::endl;
}
break;
}
}
}
}
if(events[i].events&EPOLLOUT){
auto it=fd_db.find(fd);
FdWrap *self=nullptr;
if(it!=fd_db.end()){
self=it->second;
self->fd_status=FD_CONNECTED;
epoll_ctl_mod(epfd,fd,EPOLLET|EPOLLIN);
//simple test, flush all
if(self->wb.size()>0){
write(fd,self->wb.data(),self->wb.size());
self->wb.clear();
}
}
}
}
}
std::cout<<"active: "<<fd_db.size()<<std::endl;
while(!fd_db.empty()){
auto it=fd_db.begin();
FdWrap *self=it->second;
fd_db.erase(it);
if(self->fd>0){
epoll_ctl_del(epfd,self->fd);
close(self->fd);
}
delete self;
}
epoll_ctl_del(epfd,listen_fd);
close(listen_fd);
close(epfd);
return 0;
}
Test on mininet
topo.py
#!/usr/bin/python
from mininet.topo import Topo
from mininet.net import Mininet
from mininet.cli import CLI
from mininet.link import TCLink
import time
import datetime
import subprocess
import os,signal
import sys
# 1.0 2.0 3.0
# h1----s1----h2------h3-------h4
nonbottlebw1=20
bottleneckbw=6
nonbottlebw2=100
buffer_size =bottleneckbw*1000*30/(1500*8)
net = Mininet( cleanup=True )
h1 = net.addHost('h1',ip='10.0.1.1')
h2 = net.addHost('h2',ip='10.0.1.2')
h3 = net.addHost('h3',ip='10.0.2.2')
h4 = net.addHost('h4',ip='10.0.3.2')
s1 = net.addSwitch( 's1' )
c0 = net.addController('c0')
net.addLink(h1,s1,intfName1='h1-eth0',intfName2='s1-eth0',cls=TCLink , bw=nonbottlebw1, delay='10ms', max_queue_size=10*buffer_size)
net.addLink(s1,h2,intfName1='s1-eth1',intfName2='h2-eth0',cls=TCLink , bw=nonbottlebw1, delay='10ms', max_queue_size=10*buffer_size)
net.addLink(h2,h3,intfName1='h2-eth1',intfName2='h3-eth0',cls=TCLink , bw=bottleneckbw, delay='10ms', max_queue_size=buffer_size)
net.addLink(h3,h4,intfName1='h3-eth1',intfName2='h4-eth0',cls=TCLink , bw=nonbottlebw2, delay='10ms', max_queue_size=10*buffer_size)
net.build()
h1.cmd("ifconfig h1-eth0 10.0.1.1/24")
h1.cmd("route add default gw 10.0.1.2 dev h1-eth0")
h1.cmd('sysctl net.ipv4.ip_forward=1')
#nat
#h2.cmd("iptables -t nat -N MY_TCP")
#h2.cmd("iptables -t nat -A PREROUTING -j MY_TCP")
#h2.cmd("iptables -t nat -A MY_TCP -p tcp -d 10.0.3.2 -j REDIRECT --to-ports 3333")
# tpproxy
h2.cmd("iptables -t mangle -N DIVERT")
h2.cmd("iptables -t mangle -A PREROUTING -p tcp -m socket -j DIVERT")
h2.cmd("iptables -t mangle -A DIVERT -j MARK --set-mark 1")
h2.cmd("iptables -t mangle -A DIVERT -j ACCEPTT")
h2.cmd("ip rule add fwmark 1 lookup 100")
h2.cmd("ip route add local 0.0.0.0/0 dev lo table 100")
h2.cmd("iptables -t mangle -A PREROUTING -p tcp -d 10.0.2.2 --dport 3345 -j TPROXY --tproxy-mark 0x1/0x1 --on-port 2233")
h2.cmd("ifconfig h2-eth0 10.0.1.2/24")
h2.cmd("ifconfig h2-eth1 10.0.2.1/24")
h2.cmd("ip route add to 10.0.1.0/24 via 10.0.1.1")
h2.cmd("ip route add to 10.0.2.0/24 via 10.0.2.2")
h2.cmd("ip route add to 10.0.3.0/24 via 10.0.2.2")
h2.cmd('sysctl net.ipv4.ip_forward=1')
h3.cmd("ifconfig h3-eth0 10.0.2.2/24")
h3.cmd("ifconfig h3-eth1 10.0.3.1/24")
h3.cmd("ip route add to 10.0.1.0/24 via 10.0.2.1")
h3.cmd("ip route add to 10.0.2.0/24 via 10.0.2.1")
h3.cmd("ip route add to 10.0.3.0/24 via 10.0.3.2")
h3.cmd('sysctl net.ipv4.ip_forward=1')
h4.cmd("ifconfig h4-eth0 10.0.3.2/24")
h4.cmd("route add default gw 10.0.3.1 dev h4-eth0")
h4.cmd('sysctl net.ipv4.ip_forward=1')
net.start()
time.sleep(1)
CLI(net)
net.stop()
Result
tcpdump -i h3-eth0 -w dump.pcap
Reference:
[1] go-tproxy
[2] 绑定非本机地址与透明代理
[3]TProxy - Transparent proxying
[4] IP_TRANSPARENT usage
[5] using-iptables-tproxy-instead-of-redirect
[6] iptables:tproxy做透明代理
[7] Linux透明代理 —— 使用iptables实现TCP透明代理
边栏推荐
猜你喜欢
如何编辑VirtualLab Fusion结果的格式
十天学习Unity3D脚本(一)九个回调
STM32LL library use - SPI communication
MATLAB绘图函数ezplot入门详解
光波导应用中的真实光栅效应
剑指offer:合并两个排序的链表
6.统一记录日志
Introduction to in-order traversal (non-recursive, recursive) after binary tree traversal
mysql学习总结 & 索引
Detailed introduction to the hierarchical method of binary tree creation
随机推荐
Open the door of electricity "Circuit" (1): voltage, current, reference direction
测试用例练习
shader入门精要3
What are IPV4 and IPV6?
奇技淫巧-位运算
Installation and configuration of Spark and related ecological components - quick recall
3.用户上传头像
VirtualLab Fusion中的可视化设置
MMD->Unity一站式解决方案
Optisystem应用:光电检测器灵敏度建模
Qt | 显示网络图片 QNetworkAccessManager
二叉树的遍历:递归法/ 迭代法/ 统一迭代法(强QAQ)
px和em和rem的区别
【离散化+前缀和】Acwing802. 区间和
仿真结果的格式&定制
Detailed explanation of MATLAB drawing function plot
4.发布帖子,评论帖子
Detailed introduction to drawing complex surfaces using the plot_surface command
二叉排序树与 set、map
6.统一记录日志