Add demo for use tun

For MacOS, it only support tun. If use tun than tap, server
should recode ip of each endpoint. And parse ipv4 header
from net connection, and forword packet according to ipv4
header dst ip. If dst ip included by vpn cidr cheack the
endpoint and forward to it. If dst ip included by vpn cidr
but no recode of endpoint, drop it do nothing. If dst ip
not included by vpn cidr, send it to server tun interface,
it will forword by server according to routes and iptables
rules.
This commit is contained in:
lucheng
2024-08-05 13:46:49 +08:00
parent c3e8b29a83
commit b6c51a357c
2 changed files with 199 additions and 1 deletions

198
demo/tun/main.go Normal file
View File

@@ -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)
}
}

2
go.mod
View File

@@ -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
)