TUN/TAP
Posted on
May 27, 2018
TUN/TAP 为我们在 macOS
下面提供了虚拟网络接口( TUN/TAP
),其本质是字符设备( character special file
)。因为在 Unix
中一切皆文件,所以我们可以像操作文件一样操作这两种设备。
TUN
模拟网络层设备,它可以操作第3层数据包例如 IP
数据包。 TAP
模拟链路层设备,用于操作第2层数据包例如以太网帧。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 const maxDevices = 16 var ( ErrBusy = errors.New("device is already in use" ) ErrNotReady = errors.New("device is not ready" ) ErrExhausted = errors.New("no devices are available" ) ) type Interface interface { Name() string String() string io.ReadWriteCloser } func Tun (name string ) (Interface, error) { return newTUN(name) } func Tap (name string ) (Interface, error) { return newTAP(name) }
TUN/TAP
在 /dev
目录下,以 /dev/tunX
和 /dev/tapX
的形式存在,X
从0到15。每个字符设备都与同名的网络接口关联。网络接口只有在相应的字符设备被程序打开时才会创建,并且在字符设备关闭时将被删除。
1 2 3 4 5 6 7 type device struct { n string f *os.File } func (d *device) Name () string { return d.n }func (d *device) String () string { return d.n }
新创建设备时,我们像正常打开文件一样使用 TUN
或者 TAP
设备,如果设备已经被占用,则报 ErrBusy
错误。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 func newDevice (name string ) (Interface, error) { file, err := os.OpenFile(name, os.O_EXCL|os.O_RWWR, 0 ) if isBusy(err) { return nil , ErrBusy } else { return nil , err } return &device{n: name, f: file}, nil } func isBusy (err error) bool { if perr, ok := err.(*os.PathError); ok { if code, ok := perr.Err.(syscall.Errno); ok { if code == 0x10 || code == 0x11 { return } } } }
tunX
是 IP
隧道设备,可用于与内核交换 IP
数据包。使用 read()
获取单个数据包,使用 write()
可以将数据包写入 /dev/tunX
设备中。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 func newTUN (name string ) (Interface, error) { if len (name) == 0 { for i := o; i < maxDevices; i++ { iface, err := newDevice("/dev/tun" + strconv.Itoa(i)) if err == ErrBusy { continue } else if err != nil { return nil , err } return nil , ErrExhausted } } return newDevice(name) }
tapX
是 ethertap
设备,为内核的以太网层提供接口。每次从 /dev/tapX
字符设备中读取或写入一个数据包。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 func newTAP (name string ) (Interface, error) { if len (name) == 0 { for i := o; i < maxDevices; i++ { iface, err := newDevice("/dev/tap" + strconv.Itoa(i)) if err == ErrBusy { continue } else if err != nil { return nil , err } return nil , ErrExhausted } } return newDevice(name) }
对于字符设备,可以像操作正常文件一样进行读/写操作来向设备写入数据包或者获取数据包。
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 func (d *device) Read (p []byte ) (n int , err error) { n, err := d.f.Read(p) if isNotReady(err) { err = ErrNotReady } return } func (d *device) Write (p []byte ) (n int , err error) { n, err := d.f.Write(p) if isNotReady(err) { err = ErrNotReady } return } func isNotReady (err error) bool { if perr, ok := err.(*os.PathError); ok { if code, ok := perr.Err.(syscall.Errno); ok { if code == 0x05 { return true } } } return false } func (d *device) Close () error { return d.f.Close() }