有一种应用场景,是建立某种传输通道,将用户的报文按需投递。建立的通道可以采用自定义的协议传输,用户的报文是IP包。以上行报文为例,其中目的地址是某个服务器,源地址是用户自己的ip。
有两种使用场景:
1、服务器是用户无法直接访问的,比如是一个内部服务器,地址不公开,所有的访问都需要被控制(比如通道建立者是可以控制的);
2、用户地址是内部地址,无法直接访问服务器,需要由代理来转。这种场景用户是受控的内部用户(比如手机入网后,无线网络分配的地址就是内部地址)。
包传递的流程大致如下:
我们来模拟实现一个这种场景的代码,先上golang版本的,比较简洁:
package main
import (
"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();
}