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 connectsetnonblocking(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 socketif(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 bufferbreak;}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 allif(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透明代理

tcp transparent proxy (IP_TRANSPARENT)相关推荐

  1. 透明代理Transparent Proxy

    透明代理Transparent Proxy 透明代理Transparent Proxy类似于普通代理,它可以使得处于局域网的主机直接访问外网.但不同之处,它不需要客户端进行任何设置.这样,客户端误以为 ...

  2. 『TCP/IP详解——卷一:协议』读书笔记——03

    2013-08-17 17:31:49 1.7 分用 分用(Demultiplexing):这是一个过程--当目的主机收到一个以太网数据帧时,数据就开始从协议栈中由底向上升,同时去掉各层协议上的报文首 ...

  3. Linux accept tcp,Linux TCP accept without SYN|ACK

    问题 I'm trying to write a TCP transparent proxy to run on Linux. I want to, upon receipt of an incomi ...

  4. tpproxy-tcp透明代理

    tcp transparent proxy Background  最近有个需求,需要在路由器设备中截获数据包,从而实现中转.按照下面的拓补,说明.我们需要在主机h2上截获h1发往h3的TCP协议包. ...

  5. 常用端口号/etc/services

    /etc/services文件存储的内容.记录一下,防止忘记. # /etc/services: # $Id: services,v 1.1 2004/10/09 02:49:18 andersen ...

  6. TCP/UDP常用端口及对应服务列表

     计算机之间依照互联网传输层TCP/IP协议不同的协议通信,都有不同的对应端口.所以,利用短信(datagram)的UDP,所采用的端口号码不一定和采用TCP的端口号码一样.以下为两种通信协议的端口 ...

  7. TCP/UDP常用端口列表

    前言 文章的表格中列举了Linux 中的服务.守护进程.和程序所使用的最常见的通信端口,该列表还可以在 /etc/services 文件中找到,更多详细信息推荐查看由互联网号码分派局(IANA)制定的 ...

  8. udo/tcp协议占用端口列表(因5060被占用而采用的新端口)

    http://zh.wikipedia.org/wiki/TCP/UDP%E7%AB%AF%E5%8F%A3%E5%88%97%E8%A1%A8 TCP/UDP端口列表 维基百科,自由的百科全书 跳转 ...

  9. 《TCP IP网络编程》阅读笔记及部分《图解 TCPIP》《图解 HTTP》补充笔记

    第一章:理解网络编程和套接字 接受连接请求的套接字(接电话) 服务器端创建的套接字又称为服务器端套接字或监听套接字. socket 套接字编程. 为了与远程计算机进行数据传输,需要连接到因特网,而编程 ...

最新文章

  1. 第一批 | Share·2015产品经理O2O论坛免费票发放啦!
  2. 支持的sql语法_PostgreSQL 12 新特性解读之一|支持 SQL/JSON path
  3. 网站使用微信扫码登录流程
  4. PyTorch-训练
  5. 运动会成绩管理系统python_Python 实现简易版成绩管理系统
  6. linux培训课程第六天:ppt以及笔记
  7. 执行ios命令_MEDUZA:一款针对iOS应用程序的通用SSL解绑工具
  8. synchronized()_深入理解synchronized
  9. 关于在Ubuntu下安装配置numpy,scipy,matplotlibm,pandas 以及sklearn
  10. ASP.NET Core 下的依赖注入(一)
  11. 基于八叉树的区域增长点云分割算法
  12. Linux之DHCP服务及配置
  13. c语言不用临时变量交换两个数程序分析
  14. 发票上的计算机字体,发票代码和发票号码是什么字体
  15. echarts中使用饼状图显示百分比
  16. 为什么SEM竞价推广效果越来越差?
  17. Hi-Survey Road V2.04更新说明及授权说明
  18. The page has expired due to inactivity.Please refresh and try again.
  19. 135编辑器图片裁切功能
  20. 米的换算单位和公式_请问米和毫米之间的单位是怎么换算的?

热门文章

  1. 《代号斩》7.23正式上线链游玩家 | 动作盛宴、华丽开战
  2. 如何安装Android SDK
  3. Java多线程的sleep(休眠)
  4. qt 编译的时候把源文件复制到编译目录_[QT]读写Excel
  5. Android图表控件MPChart-柱状+折线组合图表基本使用
  6. keil5的Cannot Load Flash Programming Algorithm问题
  7. ALSA驱动中 -EPIPE,-ESTRPIPE,-EBADFD错误原因解析及对策
  8. codeMirror自定义关键词颜色颜色
  9. (req、resp、转发和重定向)day29javaEE基础查漏补缺
  10. 二维码-python/js