-
Notifications
You must be signed in to change notification settings - Fork 0
/
main.go
149 lines (123 loc) · 3.57 KB
/
main.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
package main
import (
"flag"
"fmt"
"golang.org/x/sys/unix"
"net"
"os"
"unsafe"
)
const (
// sizeof(struct ifreq)
IfReqSize = 40
)
// let's open the TUN device
// A tun device is a bit wonky in that you have to first open "/dev/net/tun"
// then run a IOCTL syscall to turn the fd returned for the desired network tun device.
// This code makes use of some unsafe golang code, this is merely to avoid pulling in
// dependencies since this is for demonstration
func openTunDevice(dev string) (*os.File, error) {
fd, err := unix.Open("/dev/net/tun", os.O_RDWR, 0)
if err != nil {
return nil, err
}
// IOCTL for TUN requires the ifreq struct
// https://elixir.bootlin.com/linux/v5.8.10/source/include/uapi/linux/if.h#L234
// we fill in the required struct members such as the device name & that it is a TUN
var ifr [IfReqSize]byte
copy(ifr[:], dev)
*(*uint16)(unsafe.Pointer(&ifr[unix.IFNAMSIZ])) = unix.IFF_TUN
_, _, errno := unix.Syscall(
unix.SYS_IOCTL,
uintptr(fd),
uintptr(unix.TUNSETIFF),
uintptr(unsafe.Pointer(&ifr[0])),
)
if errno != 0 {
return nil, fmt.Errorf("error syscall.Ioctl(): %v\n", errno)
}
unix.SetNonblock(fd, true)
return os.NewFile(uintptr(fd), "/dev/net/tun"), nil
}
func main() {
port := flag.Int("port", 1234, "The protocol port for lametun")
dev := flag.String("device", "tun0", "The TUN device name")
listen := flag.Bool("listen", false, "Whether to designate this machine as the server")
server := flag.String("server", "", "The server to connect to")
flag.Parse()
fmt.Printf("listen:%v server:%v dev:%v port:%v\n", *listen, *server, *dev, *port)
if *listen && *server != "" {
fmt.Fprintf(os.Stderr, "Cannot listen and set server flag\n")
os.Exit(1)
}
if !*listen && *server == "" {
fmt.Fprintf(os.Stderr, "You must specify the server or mark this host to listen\n")
os.Exit(1)
}
tun, err := openTunDevice(*dev)
if err != nil {
panic(err)
}
conn, err := net.ListenUDP("udp4", &net.UDPAddr{Port: *port})
if err != nil {
panic(err)
}
defer conn.Close()
var raddr net.Addr
quit := make(chan struct{})
if *server != "" {
raddr, err = net.ResolveUDPAddr("udp", fmt.Sprintf("%s:%d", *server, *port))
if err != nil {
panic(err)
}
}
go func() {
// we make sure to pick a buffer size at least greater than our MTU
// 2048 is much larger :)
buffer := make([]byte, 2048)
for {
bytes, addr, err := conn.ReadFromUDP(buffer)
if err != nil {
fmt.Fprintf(os.Stderr, "error reading from UDP connection: %v\n", err)
break
}
fmt.Printf("Writing %d bytes to the tun device.\n", bytes)
raddr = addr
// write to the tun device
_, err = tun.Write(buffer[:bytes])
if err != nil {
fmt.Fprintf(os.Stderr, "error writing to tun: %v\n", err)
break
}
}
// signal to terminate
quit <- struct{}{}
}()
go func() {
for {
// we make sure to pick a buffer size at least greater than our MTU
// 2048 is much larger :)
buffer := make([]byte, 2048)
bytes, err := tun.Read(buffer)
if err != nil {
fmt.Fprintf(os.Stderr, "error reading from tun: %v\n", err)
break
}
fmt.Printf("Read %d bytes from the tun device.\n", bytes)
if raddr == nil {
fmt.Printf("UDP connection to server has not been established yet.\n")
continue
}
// at this point the buffer is a complete UDP packet; let's forward it to our UDP peer
_, err = conn.WriteTo(buffer[:bytes], raddr)
if err != nil {
fmt.Fprintf(os.Stderr, "error writing to UDP connection: %v\n", err)
break
}
}
// signal to terminate
quit <- struct{}{}
}()
// wait until an error is given
<-quit
}