Linux虚拟网络之tun(三)隔离网络下的Raw转发

来源:互联网 发布:smartgit linux 安装 编辑:程序博客网 时间:2024/06/12 01:11

在前一篇Linux虚拟网络之tun(二)Raw包转发中,我们在同一个虚机上建立了两个tun网卡,在两个网卡间借用agent_up来ping http_svr。

在实际组网环境中,其实比这个要复杂的多,而且一般也不会是在同一个虚机上的多个网卡间做这种转发(同一个虚机上,直接处理不就完了吗~~~~)。本文在前文基础上,构建一个完全隔离的网络环境,实现内部隧道的建立,转发应用层的报文。

组网模型如下:

Created with Raphaël 2.1.0应用程序应用程序client 1.1.1.xclient 1.1.1.x节点1接口 172.17.0.1节点1接口 172.17.0.1节点2接口 172.17.0.2节点2接口 172.17.0.2server 1.1.3.1server 1.1.3.1应用服务器应用服务器应用程序数据内部协议处理,净荷是应用程序IP包网络传输网络传输内部协议处理,净荷是应用程序IP包应用程序IP报文应用程序数据应用程序应答数据内部协议处理,净荷是应用程序IP包网络传输网络传输内部协议处理,净荷是应用程序IP包应用程序IP报文应用程序数据

稍微有点绕,解释一下(以下将建立和维护这套传输管道的系统简称为系统):

  1. client是系统给应用程序(用户)分配的地址。应用程序的上行报文通过client接口传输,系统监听client的所有报文。每个client实际上是用一个tun/tap网卡来实现的。
  2. 是不是一定要有client?应用程序不能直接发给对外接口网卡吗?其实是可以的。不过有了client接口,一方面会更容易扩展,想象空间更多;一方面接口的处理可能会简单一些。
  3. 节点2对外接口上收到的上行报文,经过内部协议的处理后,可以直接投递给服务器。在server接口上处理内部协议,还是在节点对外网卡收到包直接处理,其实差别不大,上图的处理稍微简单一些。
  4. 下行报文处理跟上行正好反过来。
  5. 限于测试环境,没有多个测试机,所以用容器来隔离。图中的对外接口地址就是容器的地址。

来一份golang的代码:

  • common.go 实现一些公用函数
package commonimport (    "fmt"    "net")type CommonRecever interface {    Read(p []byte) (n int, err error)    Close() error}func RunRecever(recever CommonRecever, name string, action func([]byte)) {    go func() {        buff := make([]byte, 8*1024)        for {            num, err := recever.Read(buff)            if err != nil {                fmt.Printf("%s read err: %v \n", name, err)                recever.Close()                return            }            action(buff[:num])        }    }()}func UdpSender(rip string) (*net.UDPConn, error) {    ripaddr, err := net.ResolveUDPAddr("udp4", rip)    if err != nil {        fmt.Println("net.ResolveUDPAddr ", err)        return nil, err    }    conn, err := net.DialUDP("udp4", nil, ripaddr)    if err != nil {        fmt.Println("net.DialIP ", err)        return nil, err    }    return conn, nil}func UdpRecver(lip string) (*net.UDPConn, error) {    lipaddr, err := net.ResolveUDPAddr("udp4", lip)    if err != nil {        fmt.Println("net.ResolveUDPAddr ", err)        return nil, err    }    conn, err := net.ListenUDP("udp4", lipaddr)    if err != nil {        fmt.Println("net.ListenUDP ", err)        return nil, err    }    return conn, nil}
  • client.go 客户端代码
package mainimport (    "fmt"    "os"    "os/exec"    "os/signal"    "syscall"    "common"    "github.com/songgao/water")func main() {    client, err := water.NewTUN("client")    if err != nil {        fmt.Println(err)    }    defer client.Close()    // 模拟终端地址1    if err = exec.Command("ip", "addr", "add", "1.1.1.1/32", "dev", "client:0", "peer", "1.1.3.1").Run(); err != nil {        fmt.Println(err)    }    // 模拟终端地址2    if err = exec.Command("ip", "addr", "add", "1.1.1.2/32", "dev", "client:1", "peer", "1.1.3.1").Run(); err != nil {        fmt.Println(err)    }    if err := exec.Command("ip", "link", "set", "dev", "client", "up").Run(); err != nil {        fmt.Println(err)    }    dockerSender, err := common.UdpSender("172.17.0.2:20170")    if err != nil {        fmt.Println("docker udp sender ", err)    }    defer dockerSender.Close()    dockerRecver, err := common.UdpRecver("172.17.0.1:20170")    if err != nil {        fmt.Println("docker udp recver ", err)    }    defer dockerRecver.Close()    common.RunRecever(client, "client", func(msg []byte) {        dockerSender.Write(msg)    })    common.RunRecever(dockerRecver, "docker", func(msg []byte) {        client.Write(msg)    })    quitChan := make(chan os.Signal)    signal.Notify(quitChan,        syscall.SIGINT,        syscall.SIGTERM,        syscall.SIGHUP,    )    <-quitChan}
  • server.go 服务端代码
package mainimport (    "fmt"    "os"    "os/exec"    "os/signal"    "syscall"    "github.com/songgao/water"    "common")func main() {    fmt.Println("start server!!!")    // server interface    serverTun, err := water.NewTUN("server")    if err != nil {        fmt.Println(err)    }    defer serverTun.Close()    if err = exec.Command("ip", "addr", "add", "1.1.3.1/32", "dev", "server:0").Run(); err != nil {        fmt.Println(err)    }    if err := exec.Command("ip", "link", "set", "dev", "server", "up").Run(); err != nil {        fmt.Println(err)    }    if err := exec.Command("ip", "route", "add", "1.1.1.0/24", "dev", "server").Run(); err != nil {        fmt.Println(err)    }    dockerSender, err := common.UdpSender("172.17.0.1:20170")    if err != nil {        fmt.Println("docker udp sender ", err)    }    dockerRecver, err := common.UdpRecver("172.17.0.2:20170")    if err != nil {        fmt.Println("docker udp recver ", err)    }    fmt.Println("server start handle msg!!!")    common.RunRecever(dockerRecver, "docker", func(msg []byte) {        serverTun.Write(msg)    })    common.RunRecever(serverTun, "server", func(msg []byte) {        dockerSender.Write(msg)    })    quitChan := make(chan os.Signal)    signal.Notify(quitChan,        syscall.SIGINT,        syscall.SIGTERM,        syscall.SIGHUP,    )    <-quitChan}

执行方式:

  1. 在主机执行 client.go;
  2. 在容器中执行server.go;
  3. 在主机执行 ping 1.1.3.1 -I 1.1.1.1 就会执行从 1.1.1.1 到 1.1.3.1 的ping操作; 执行 ping 1.1.3.1 -I 1.1.1.2 就会执行从 1.1.1.2 到 1.1.3.1 的ping操作;
  4. 在容器中的1.1.3.1地址上建立一个http的服务器,放几个可供下载的文件,比如 test.zip
  5. 在主机上执行 wget --bind-address=1.1.1.1 --no-proxy http://1.1.3.1/test.zip 测试下载

    遗留问题:
    1、如果每个模拟终端是独立的网卡,但是服务器是同一个,那么ping包没问题,下载会涉及到路由问题,无法成功

阅读全文
0 0
原创粉丝点击