diff --git a/demo/tun/main.go b/demo/tun/main.go new file mode 100644 index 0000000..a3adfe2 --- /dev/null +++ b/demo/tun/main.go @@ -0,0 +1,198 @@ +package main + +import ( + "fmt" + "io" + "net" + "os" + + "github.com/songgao/water" + "github.com/urfave/cli/v2" + "github.com/vishvananda/netlink" + "golang.org/x/net/ipv4" +) + +const ( + PORT = 8000 + ADDR = "10.67.0.254" + SIP = "10.69.0.254/24" + MAX_PKT_SIZE = 4096 +) + +var EP map[string]net.Conn +var idx = 1 + +func init() { + EP = make(map[string]net.Conn) +} + +func handleConn(conn net.Conn, iface *water.Interface) { + EP[fmt.Sprintf("10.69.0.%d", idx)] = conn + idx += 1 + + go func() { + for { + buf := make([]byte, MAX_PKT_SIZE) + n, err := iface.Read(buf) + if err != nil { + fmt.Println(err) + continue + } + + h, err := ipv4.ParseHeader(buf[:n]) + if err != nil { + fmt.Println(err) + continue + } + dst := h.Dst.String() + fmt.Printf("iface dst %s\n", dst) + p, ok := EP[dst] + if !ok { + fmt.Printf("pkt from server dst %s no route\n", dst) + continue + } + + _, err = p.Write(buf[:n]) + if err != nil { + fmt.Println(err) + continue + } + } + }() + + for { + buf := make([]byte, MAX_PKT_SIZE) + n, err := conn.Read(buf) + if err != nil { + fmt.Println(err) + continue + } + + h, err := ipv4.ParseHeader(buf[:n]) + if err != nil { + fmt.Println(err) + continue + } + dst := h.Dst.String() + fmt.Printf("conn dst %s\n", dst) + + p, ok := EP[dst] + if !ok { + fmt.Printf("pkt from client dst %s send to loacal", dst) + _, err = iface.Write(buf[:n]) + if err != nil { + fmt.Println(err) + continue + } + } else { + _, err = p.Write(buf[:n]) + if err != nil { + fmt.Println(err) + continue + } + } + } +} + +func setupTun(addr string) (*water.Interface, error) { + tname := "tun0" + config := water.Config{ + DeviceType: water.TUN, + } + config.Name = tname + + iface, err := water.New(config) + if err != nil { + return nil, err + } + + link, err := netlink.LinkByName(tname) + if err != nil { + return nil, err + } + + a, err := netlink.ParseAddr(addr) + if err != nil { + return nil, err + } + + err = netlink.AddrAdd(link, a) + if err != nil { + return nil, err + } + + err = netlink.LinkSetUp(link) + if err != nil { + return nil, err + } + + return iface, nil +} + +func sRun(cCtx *cli.Context) error { + ln, err := net.Listen("tcp4", fmt.Sprintf(":%d", PORT)) + if err != nil { + return err + } + fmt.Println("server listen on port 8000") + + iface, err := setupTun(SIP) + if err != nil { + return err + } + fmt.Printf("create tun %s\n", iface.Name()) + + for { + conn, err := ln.Accept() + if err != nil { + return err + } + + go handleConn(conn, iface) + } +} + +func cRun(cCtx *cli.Context) error { + conn, err := net.Dial("tcp", fmt.Sprintf("%s:%d", ADDR, PORT)) + if err != nil { + return err + } + fmt.Println("client dial 8000") + + iface, err := setupTun(cCtx.String("addr")) + if err != nil { + return err + } + + for { + go io.Copy(iface, conn) + io.Copy(conn, iface) + } +} + +func main() { + app := &cli.App{ + Commands: []*cli.Command{ + { + Name: "server", + Action: sRun, + }, + { + Name: "client", + Action: cRun, + Flags: []cli.Flag{ + &cli.StringFlag{ + Name: "addr", + Aliases: []string{"a"}, + Required: true, + }, + }, + }, + }, + } + + if err := app.Run(os.Args); err != nil { + fmt.Println(err) + os.Exit(1) + } +} diff --git a/go.mod b/go.mod index db61da2..552cdcc 100644 --- a/go.mod +++ b/go.mod @@ -12,6 +12,7 @@ require ( github.com/songgao/water v0.0.0-20200317203138-2b4b6d7c09d8 github.com/urfave/cli/v2 v2.27.1 github.com/vishvananda/netlink v1.1.0 + golang.org/x/net v0.10.0 golang.org/x/sync v0.7.0 golang.org/x/sys v0.19.0 golang.org/x/term v0.19.0 @@ -41,7 +42,6 @@ require ( github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect golang.org/x/arch v0.3.0 // indirect golang.org/x/crypto v0.9.0 // indirect - golang.org/x/net v0.10.0 // indirect golang.org/x/text v0.9.0 // indirect google.golang.org/protobuf v1.30.0 // indirect )