-
Notifications
You must be signed in to change notification settings - Fork 1
/
utun_darwin.go
156 lines (134 loc) · 3.97 KB
/
utun_darwin.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
150
151
152
153
154
155
156
package tun_macos
import (
"fmt"
"os"
"strconv"
"strings"
"syscall"
"unsafe"
)
/*
// Headers
#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/sys_domain.h>
#include <sys/kern_control.h>
#include <net/if_utun.h>
*/
import "C"
const (
ifacePrefix = "utun"
uTunControlName = C.UTUN_CONTROL_NAME // net/if_utun.h -> "com.apple.net.utun_control"
)
// ctlInfo
// kern_control.h
/*
#define MAX_KCTL_NAME 96
struct ctl_info {
u_int32_t ctl_id; // Kernel Controller ID
char ctl_name[MAX_KCTL_NAME]; // Kernel Controller Name (a C string)
};
*/
type ctlInfo struct {
CtlId uint32
CtlName [C.MAX_KCTL_NAME]byte
}
// sockAddrCtl
// kern_control.h
/*
struct sockaddr_ctl {
u_char sc_len; // depends on size of bundle ID string
u_char sc_family; //AF_SYSTEM
u_int16_t ss_sysaddr; // AF_SYS_KERNCONTROL
u_int32_t sc_id; // Controller unique identifier
u_int32_t sc_unit; // Developer private unit number
u_int32_t sc_reserved[5];
};
*/
type sockAddrCtl struct {
ScLen uint8
ScFamily uint8
ScSysAddr uint16
ScId uint32
ScUnit uint32
ScReversed uint32
}
func Open(name string) (*os.File, error) {
after, found := strings.CutPrefix(name, ifacePrefix)
if !found {
return nil, fmt.Errorf("interface name must be utun[0-9]")
}
unit, err := strconv.Atoi(after)
if err != nil {
return nil, fmt.Errorf("interface name must have unit number")
}
fd, err := syscall.Socket(syscall.AF_SYSTEM, syscall.SOCK_DGRAM, C.SYSPROTO_CONTROL)
if err != nil {
return nil, err
}
var errNo syscall.Errno
var ctlInfo = &ctlInfo{}
copy(ctlInfo.CtlName[:], []byte(uTunControlName))
/**
if (ioctl(fd(), CTLIOCGINFO, &ctlInfo) == -1)
throw utun_error("ioctl(CTLIOCGINFO)");
*/
// kern_control.h
// #define CTLIOCGINFO _IOWR('N', 3, struct ctl_info) /* get id from name */
// ...
// sys/ioccom.h
// #define _IOWR(g, n, t) _IOC(IOC_INOUT, (g), (n), sizeof(t))
_, _, errNo = syscall.Syscall(syscall.SYS_IOCTL, uintptr(fd), C.CTLIOCGINFO, uintptr(unsafe.Pointer(ctlInfo)))
if errNo != 0 {
return nil, fmt.Errorf("error while calling SYS_IOCTL ErrNo: %v", errNo)
}
sockAddrCtl := &sockAddrCtl{
ScLen: uint8(ctlInfo.CtlId),
ScFamily: syscall.AF_SYSTEM,
// sys_domain.h
// #define AF_SYS_CONTROL 2 /* corresponding sub address type */
ScSysAddr: C.AF_SYS_CONTROL,
ScId: uint32(unit + 1),
ScReversed: 0,
}
/**
https://go.dev/src/syscall/asm_darwin_amd64.s
Difference between Syscall and RawSyscall,
Syscall notifies to runtime for blocking syscall operations and yield CPU-time to another goroutine, coordinates with the scheduler
Whereas RawSyscall does not notify runtime, does not coordinate with the scheduler.
So using RawSyscall may block other goroutines from running.
*/
/**
if (connect(fd(), (struct sockaddr *)&sc, sizeof(sc)) == -1)
return -1;
*/
_, _, errNo = syscall.RawSyscall(syscall.SYS_CONNECT, uintptr(fd), uintptr(unsafe.Pointer(sockAddrCtl)), unsafe.Sizeof(sockAddrCtl))
if errNo != 0 {
return nil, fmt.Errorf("error while calling SYS_CONNECT ErrNo: %v", errNo)
}
/**
// Get iface name of newly created utun dev.
char utunname[20];
socklen_t utunname_len = sizeof(utunname);
if (getsockopt(fd(), SYSPROTO_CONTROL, UTUN_OPT_IFNAME, utunname, &utunname_len))
throw utun_error("getsockopt(SYSPROTO_CONTROL)");
*/
var uTunName [20]byte
_, _, errNo = syscall.Syscall6(
syscall.SYS_GETSOCKOPT,
C.SYSPROTO_CONTROL,
uintptr(fd),
C.UTUN_OPT_IFNAME,
uintptr(unsafe.Pointer(&uTunName)),
unsafe.Sizeof(uTunName),
0,
)
if errNo != 0 {
return nil, fmt.Errorf("error while calling SYS_GETSOCKOPT ErrNo: %v", errNo)
}
// read and write operations on the file will not block the program's execution
if err = syscall.SetNonblock(fd, true); err != nil {
return nil, fmt.Errorf("error while setting non-block mode for file")
}
return os.NewFile(uintptr(fd), string(uTunName[:])), nil
}