Linux虚拟网络之tun(二)Raw包转发

来源:互联网 发布:网络学英语 编辑:程序博客网 时间:2024/06/05 09:57

有一种应用场景,是建立某种传输通道,将用户的报文按需投递。建立的通道可以采用自定义的协议传输,用户的报文是IP包。以上行报文为例,其中目的地址是某个服务器,源地址是用户自己的ip。

有两种使用场景:
1、服务器是用户无法直接访问的,比如是一个内部服务器,地址不公开,所有的访问都需要被控制(比如通道建立者是可以控制的);
2、用户地址是内部地址,无法直接访问服务器,需要由代理来转。这种场景用户是受控的内部用户(比如手机入网后,无线网络分配的地址就是内部地址)。

包传递的流程大致如下:

Created with Raphaël 2.1.0用户用户代理代理服务器服务器用内部协议头封装的报文,payload是IP包去掉内部协议头后将IP包转发出去处理结束后,应答的IP包用内部协议头封装后的报文,payload是IP包

我们来模拟实现一个这种场景的代码,先上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
原创粉丝点击