Linux虚拟网络之tun(二)Raw包转发
来源:互联网 发布:网络学英语 编辑:程序博客网 时间:2024/06/05 09:57
有一种应用场景,是建立某种传输通道,将用户的报文按需投递。建立的通道可以采用自定义的协议传输,用户的报文是IP包。以上行报文为例,其中目的地址是某个服务器,源地址是用户自己的ip。
有两种使用场景:
1、服务器是用户无法直接访问的,比如是一个内部服务器,地址不公开,所有的访问都需要被控制(比如通道建立者是可以控制的);
2、用户地址是内部地址,无法直接访问服务器,需要由代理来转。这种场景用户是受控的内部用户(比如手机入网后,无线网络分配的地址就是内部地址)。
包传递的流程大致如下:
我们来模拟实现一个这种场景的代码,先上golang版本的,比较简洁:
package mainimport ( "net" "os/exec" "fmt" "github.com/mdlayher/raw" "github.com/songgao/water")func AddIpToIntf(ifName string, ip string) error { if err := exec.Command("ip", "addr", "add", ip, "dev", ifName).Run(); err != nil { fmt.Println(err) return err } if err := exec.Command("ip", "link", "set", "dev", ifName, "up").Run(); err != nil { fmt.Println(err) } return nil}func NewTUNIntf(ifName string, ip string) (ifce *water.Interface, err error) { intf, err := water.NewTUN(ifName) if err != nil { return nil, err } return intf, AddIpToIntf(ifName, ip)}func main() { _, err := NewTUNIntf("http_svr", "1.1.2.1/24") if err != nil { fmt.Println(err) } agentUpIntf, err := NewTUNIntf("agent_up", "1.1.1.1/24") if err != nil { fmt.Println(err) } ipaddr, err := net.ResolveIPAddr("ip4", "1.1.2.1") if err != nil { fmt.Println("net.ResolveIPAddr ", err) } conn, err := net.DialIP("ip4:255", nil, ipaddr) if err != nil { fmt.Println("net.DialIP ", err) } data := make([]byte, 1024*8) for { num, err := agentUpIntf.Read(data) if err != nil { fmt.Println("agent up recv ", err) } if num > 0 { fmt.Println("agent up recv data len:", num, ", data:", data[:20]) } num, err = conn.Write(data[:num]) if err != nil { fmt.Println("write to http conn", err) } if num > 0 { fmt.Println("write to http conn data len:", num, ", data:", data[:20]) } }}
1、NewTUNIntf是创建虚拟网卡的,连带生效一个ip地址;
2、net.DialIP创建一个raw类型的socket,特别要注意参数“ip4:255”,其中的255表示IPPROTO_RAW。协议类型非常重要,否则下面的报文是无法转发出去的(因为转发的IP包)。
3、从agentUpIntf收到的包,直接发给http服务器。
上面的代码有个问题没有体现:用户的报文从哪里来?
实际环境中,报文的来源有很多方式,不在这里的讨论范围。不过常见的一种,是 agent从另一个网卡接收用户数据。
好了,稍微验证一下,在shell里面运行:
ping 1.1.2.1 -I agent_up
就能看到ping包是通的。
现在来个c版本的代码:
#include <fcntl.h>#include <sys/socket.h>#include <linux/ip.h>#include <linux/if.h>#include <linux/if_tun.h>#include <arpa/inet.h>#include <string.h>#include <sys/ioctl.h>#include <unistd.h>#include <iostream>#include <errno.h>#include <linux/if_ether.h>#include <linux/if_packet.h>#include <thread>using namespace std;int NewTUNIntf(char* dev, int net_addr){ int tun_fd; struct ifreq ifr; tun_fd = open("/dev/net/tun", O_RDWR); if(0 > tun_fd) { cout<<"open tun file error!" << endl; return tun_fd; } memset(&ifr, 0, sizeof(ifr)); ifr.ifr_flags = IFF_TUN | IFF_NO_PI; strncpy(ifr.ifr_ifrn.ifrn_name, dev, IFNAMSIZ); if(0 > ioctl(tun_fd, TUNSETIFF, &ifr)) { string err_str = strerror(errno); cout << "Failed to set TUN device name: " << err_str << endl; close(tun_fd); return tun_fd; } // Bring up the interface int sock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL)); if(0 > ioctl(sock, SIOCGIFFLAGS, &ifr)) { string err_str = strerror(errno); cout << "Failed to bring up socket: " << err_str << endl; close(tun_fd); return tun_fd; } ifr.ifr_flags |= IFF_UP | IFF_RUNNING | IFF_PROMISC; if(0 > ioctl(sock, SIOCSIFFLAGS, &ifr)) { string err_str = strerror(errno); cout << "Failed to set socket flags: " << err_str << endl; close(tun_fd); return tun_fd; } ifr.ifr_addr.sa_family = AF_INET; ((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr.s_addr = net_addr; if(0 > ioctl(sock, SIOCSIFADDR, &ifr)) { string err_str = strerror(errno); cout << "Failed to set socket address: " << err_str << endl; close(tun_fd); return tun_fd; } ifr.ifr_netmask.sa_family = AF_INET; ((struct sockaddr_in *)&ifr.ifr_netmask)->sin_addr.s_addr = inet_addr("255.255.255.0"); if(0 > ioctl(sock, SIOCSIFNETMASK, &ifr)) { string err_str = strerror(errno); cout << "Failed to set socket netmask: " << err_str << endl; close(tun_fd); return tun_fd; }}void task(char* dev, int net_addr){ int tun_fd = NewTUNIntf(dev, net_addr); int sd = -1; if ((sd = socket (AF_INET, SOCK_RAW, IPPROTO_RAW)) < 0) { printf("getting socket error: %s", strerror(errno)); return; } // 这个设置不影响结果// const int on = 1;// if (setsockopt (sd, IPPROTO_IP, IP_HDRINCL, &on, sizeof (on)) < 0)// {// printf("set socket %d if %s opt IP_HDRINCL error: %s", sd, "2152", strerror(errno));// close (sd);// return;// } struct sockaddr_in sin; memset (&sin, 0, sizeof (struct sockaddr_in)); sin.sin_family = AF_INET; if (!inet_pton(AF_INET, "172.23.2.24", &sin.sin_addr)) { printf("send inet_pton failed!"); return; } int N_bytes; unsigned char msg[8000]; in_addr ip_addr; ip_addr.s_addr = net_addr; while(true) { N_bytes = read(tun_fd, msg, 8000); if (N_bytes > 0) { printf("%s recv msg len: %3d, msg: ", inet_ntoa(ip_addr), N_bytes); for (int i = 0; i < 20 && i < N_bytes; i++) printf("%2x ", msg[i]); cout << endl; } if (sendto (sd, msg, N_bytes, 0, (struct sockaddr *) &sin, sizeof (struct sockaddr)) < 0) { printf("sendto %s, sendLocalSocketId : %d, error: %s", "172.23.2.24", sd, strerror(errno)); return; } }}void task2(char* dev, int net_addr){ int tun_fd = NewTUNIntf(dev, net_addr); int N_bytes; unsigned char msg[8000]; in_addr ip_addr; ip_addr.s_addr = net_addr; while(true) { N_bytes = read(tun_fd, msg, 8000); if (N_bytes > 0) { printf("%s recv msg len: %3d, msg: ", inet_ntoa(ip_addr), N_bytes); for (int i = 0; i < 20 && i < N_bytes; i++) printf("%2x ", msg[i]); cout << endl; } }}int main(){ char dev[IFNAMSIZ] = "tun1"; char dev2[IFNAMSIZ] = "tun2"; thread t(task, dev, inet_addr("172.23.1.25")); thread t2(task2, dev2, inet_addr("172.23.2.24")); t.join(); t2.join();}
1 0
- Linux虚拟网络之tun(二)Raw包转发
- Linux虚拟网络之tun(三)隔离网络下的Raw转发
- Linux虚拟网络之tun(四)虚拟VPN
- [转发]网络虚拟化技术(二): TUN/TAP MACVLAN MACVTAP
- Linux虚拟网络之tun(一)基本使用
- 网络虚拟化技术(二): TUN/TAP MACVLAN MACVTAP
- 网络虚拟化技术(二): TUN/TAP MACVLAN MACVTAP
- 网络虚拟化技术(二): TUN/TAP MACVLAN MACVTAP
- 网络虚拟化技术(二): TUN/TAP MACVLAN MACVTAP
- 网络虚拟化技术(二): TUN/TAP MACVLAN MACVTAP
- 网络虚拟化技术(二): TUN/TAP MACVLAN MACVTAP
- LINUX 虚拟网卡tun例子
- Linux虚拟网卡TUN/TAP
- Linux虚拟网卡TUN/TAP
- LINUX 虚拟网卡tun例子
- linux TUN 虚拟网卡设备
- LINUX 虚拟网卡tun例子
- linux TUN 虚拟网卡设备
- linux下将Python脚本打包为可执行文件
- ImageLoader框架实现的图片缓存
- ubuntu 默认打开方式
- Vijos 1193题:扫雷
- linux运维-vsftpd服务
- Linux虚拟网络之tun(二)Raw包转发
- 通过AngularJS实现前端与后台的数据对接——服务(service,$http)
- JUC线程池--线程池架构
- 一个ajax通用函数(xmlhttprequest封装)
- C/C++编译器预定宏使用
- Unity中默认的文件夹以及路径问题
- Windows下mysql忘记root密码的解决方法
- url 传递中文参数乱码问题
- 结构体