tun_darwin.go 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466
  1. /*
  2. * Copyright (c) 2017, Psiphon Inc.
  3. * All rights reserved.
  4. *
  5. * This program is free software: you can redistribute it and/or modify
  6. * it under the terms of the GNU General Public License as published by
  7. * the Free Software Foundation, either version 3 of the License, or
  8. * (at your option) any later version.
  9. *
  10. * This program is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU General Public License
  16. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  17. *
  18. */
  19. // Darwin utun code based on https://github.com/songgao/water:
  20. /*
  21. Copyright (c) 2016, Song Gao <song@gao.io>
  22. All rights reserved.
  23. Redistribution and use in source and binary forms, with or without
  24. modification, are permitted provided that the following conditions are met:
  25. * Redistributions of source code must retain the above copyright notice, this
  26. list of conditions and the following disclaimer.
  27. * Redistributions in binary form must reproduce the above copyright notice,
  28. this list of conditions and the following disclaimer in the documentation
  29. and/or other materials provided with the distribution.
  30. * Neither the name of water nor the names of its contributors may be used to
  31. endorse or promote products derived from this software without specific prior
  32. written permission.
  33. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  34. AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  35. IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  36. DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
  37. FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  38. DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
  39. SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  40. CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  41. OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  42. OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  43. */
  44. package tun
  45. import (
  46. std_errors "errors"
  47. "fmt"
  48. "io/ioutil"
  49. "net"
  50. "os"
  51. "strconv"
  52. "syscall"
  53. "unsafe"
  54. "github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common"
  55. "github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/errors"
  56. )
  57. const (
  58. DEFAULT_PUBLIC_INTERFACE_NAME = "en0"
  59. )
  60. func IsSupported() bool {
  61. return true
  62. }
  63. func makeDeviceInboundBuffer(MTU int) []byte {
  64. // 4 extra bytes to read a utun packet header
  65. return make([]byte, 4+MTU)
  66. }
  67. func makeDeviceOutboundBuffer(MTU int) []byte {
  68. // 4 extra bytes to write a utun packet header
  69. return make([]byte, 4+MTU)
  70. }
  71. // OpenTunDevice opens a file for performing device I/O with
  72. // either a specified tun device, or a new tun device (when
  73. // name is "").
  74. func OpenTunDevice(name string) (*os.File, string, error) {
  75. // Prevent fork between creating fd and setting CLOEXEC
  76. syscall.ForkLock.RLock()
  77. defer syscall.ForkLock.RUnlock()
  78. unit := uint32(0)
  79. if name != "" {
  80. n, err := fmt.Sscanf(name, "utun%d", &unit)
  81. if err == nil && n != 1 {
  82. err = std_errors.New("failed to scan device name")
  83. }
  84. if err != nil {
  85. return nil, "", errors.Trace(err)
  86. }
  87. }
  88. // Darwin utun code based on:
  89. // https://github.com/songgao/water/blob/70591d249921d075889cc49aaef072987e6b354a/syscalls_darwin.go
  90. // Definitions from <ioctl.h>, <sys/socket.h>, <sys/sys_domain.h>
  91. const (
  92. TUN_CONTROL_NAME = "com.apple.net.utun_control"
  93. CTLIOCGINFO = (0x40000000 | 0x80000000) | ((100 & 0x1fff) << 16) | uint32(byte('N'))<<8 | 3
  94. TUNSIFMODE = (0x80000000) | ((4 & 0x1fff) << 16) | uint32(byte('t'))<<8 | 94
  95. PF_SYSTEM = syscall.AF_SYSTEM
  96. SYSPROTO_CONTROL = 2
  97. AF_SYS_CONTROL = 2
  98. UTUN_OPT_IFNAME = 2
  99. )
  100. fd, err := syscall.Socket(
  101. PF_SYSTEM,
  102. syscall.SOCK_DGRAM,
  103. SYSPROTO_CONTROL)
  104. if err != nil {
  105. return nil, "", errors.Trace(err)
  106. }
  107. // Set CLOEXEC so file descriptor not leaked to network config command subprocesses
  108. syscall.CloseOnExec(fd)
  109. var tunControlName [96]byte
  110. copy(tunControlName[:], TUN_CONTROL_NAME)
  111. ctlInfo := struct {
  112. ctlID uint32
  113. ctlName [96]byte
  114. }{
  115. 0,
  116. tunControlName,
  117. }
  118. _, _, errno := syscall.Syscall(
  119. syscall.SYS_IOCTL,
  120. uintptr(fd),
  121. uintptr(CTLIOCGINFO),
  122. uintptr(unsafe.Pointer(&ctlInfo)))
  123. if errno != 0 {
  124. return nil, "", errors.Trace(errno)
  125. }
  126. sockaddrCtlSize := 32
  127. sockaddrCtl := struct {
  128. scLen uint8
  129. scFamily uint8
  130. ssSysaddr uint16
  131. scID uint32
  132. scUnit uint32
  133. scReserved [5]uint32
  134. }{
  135. uint8(sockaddrCtlSize),
  136. syscall.AF_SYSTEM,
  137. AF_SYS_CONTROL,
  138. ctlInfo.ctlID,
  139. unit,
  140. [5]uint32{},
  141. }
  142. _, _, errno = syscall.RawSyscall(
  143. syscall.SYS_CONNECT,
  144. uintptr(fd),
  145. uintptr(unsafe.Pointer(&sockaddrCtl)),
  146. uintptr(sockaddrCtlSize))
  147. if errno != 0 {
  148. return nil, "", errors.Trace(errno)
  149. }
  150. ifNameSize := uintptr(16)
  151. ifName := struct {
  152. name [16]byte
  153. }{}
  154. _, _, errno = syscall.Syscall6(
  155. syscall.SYS_GETSOCKOPT,
  156. uintptr(fd),
  157. SYSPROTO_CONTROL,
  158. UTUN_OPT_IFNAME,
  159. uintptr(unsafe.Pointer(&ifName)),
  160. uintptr(unsafe.Pointer(&ifNameSize)),
  161. 0)
  162. if errno != 0 {
  163. return nil, "", errors.Trace(errno)
  164. }
  165. deviceName := string(ifName.name[:ifNameSize-1])
  166. file := os.NewFile(uintptr(fd), deviceName)
  167. return file, deviceName, nil
  168. }
  169. func (device *Device) readTunPacket() (int, int, error) {
  170. // Assumes MTU passed to makeDeviceInboundBuffer is actual MTU and
  171. // so buffer is sufficiently large to always read a complete packet,
  172. // along with the 4 byte utun header.
  173. n, err := device.deviceIO.Read(device.inboundBuffer)
  174. if err != nil {
  175. return 0, 0, errors.Trace(err)
  176. }
  177. if n < 4 {
  178. return 0, 0, errors.TraceNew("missing packet prefix")
  179. }
  180. return 4, n - 4, nil
  181. }
  182. func (device *Device) writeTunPacket(packet []byte) error {
  183. // Note: can't use writev via net.Buffers. os.File isn't
  184. // a net.Conn and can't wrap with net.FileConn due to
  185. // fd type. So writes use an intermediate buffer to add
  186. // the header.
  187. // Assumes:
  188. // - device.outboundBuffer[0..2] will be 0, the zero value
  189. // - packet already validated as 4 or 6
  190. // - max len(packet) won't exceed MTU, prellocated size of
  191. // outboundBuffer.
  192. // Write utun header
  193. if len(packet) > 0 && packet[0]>>4 == 4 {
  194. device.outboundBuffer[3] = syscall.AF_INET
  195. } else { // IPv6
  196. device.outboundBuffer[3] = syscall.AF_INET6
  197. }
  198. copy(device.outboundBuffer[4:], packet)
  199. size := 4 + len(packet)
  200. _, err := device.deviceIO.Write(device.outboundBuffer[:size])
  201. if err != nil {
  202. return errors.Trace(err)
  203. }
  204. return nil
  205. }
  206. func resetNATTables(_ *ServerConfig, _ net.IP) error {
  207. // Not supported on Darwin
  208. // TODO: could use pfctl -K?
  209. return nil
  210. }
  211. func configureServerInterface(
  212. config *ServerConfig,
  213. tunDeviceName string) error {
  214. // TODO: fix or remove the following broken code
  215. return errors.Trace(errUnsupported)
  216. // Set tun device network addresses and MTU
  217. IPv4Address, IPv4Netmask, err := splitIPMask(serverIPv4AddressCIDR)
  218. if err != nil {
  219. return errors.Trace(err)
  220. }
  221. err = common.RunNetworkConfigCommand(
  222. config.Logger,
  223. config.SudoNetworkConfigCommands,
  224. "ifconfig",
  225. tunDeviceName,
  226. IPv4Address, IPv4Address, IPv4Netmask,
  227. "mtu", strconv.Itoa(getMTU(config.MTU)),
  228. "up")
  229. if err != nil {
  230. return errors.Trace(err)
  231. }
  232. IPv6Address, IPv6Prefixlen, err := splitIPPrefixLen(serverIPv6AddressCIDR)
  233. if err != nil {
  234. return errors.Trace(err)
  235. }
  236. err = common.RunNetworkConfigCommand(
  237. config.Logger,
  238. config.SudoNetworkConfigCommands,
  239. "ifconfig",
  240. tunDeviceName,
  241. "inet6", IPv6Address, "prefixlen", IPv6Prefixlen)
  242. if err != nil {
  243. return errors.Trace(err)
  244. }
  245. // NAT tun device to external interface
  246. //
  247. // Uses configuration described here:
  248. // https://discussions.apple.com/thread/5538749
  249. egressInterface := config.EgressInterface
  250. if egressInterface == "" {
  251. egressInterface = DEFAULT_PUBLIC_INTERFACE_NAME
  252. }
  253. err = common.RunNetworkConfigCommand(
  254. config.Logger,
  255. config.SudoNetworkConfigCommands,
  256. "sysctl",
  257. "net.inet.ip.forwarding=1")
  258. if err != nil {
  259. return errors.Trace(err)
  260. }
  261. err = common.RunNetworkConfigCommand(
  262. config.Logger,
  263. config.SudoNetworkConfigCommands,
  264. "sysctl",
  265. "net.inet6.ip6.forwarding=1")
  266. if err != nil {
  267. return errors.Trace(err)
  268. }
  269. // TODO:
  270. // - should use -E and preserve existing pf state?
  271. // - OR should use "-F all" to reset everything?
  272. pfConf := fmt.Sprintf(
  273. "nat on %s from %s to any -> (%s)\n"+
  274. "nat on %s from %s to any -> (%s)\n"+
  275. "pass from %s to any keep state\n"+
  276. "pass from %s to any keep state\n\n",
  277. egressInterface, privateSubnetIPv4.String(), egressInterface,
  278. egressInterface, privateSubnetIPv6.String(), egressInterface,
  279. privateSubnetIPv4.String(),
  280. privateSubnetIPv6.String())
  281. tempFile, err := ioutil.TempFile("", "tun_pf_conf")
  282. if err != nil {
  283. return errors.Trace(err)
  284. }
  285. defer os.Remove(tempFile.Name())
  286. _, err = tempFile.Write([]byte(pfConf))
  287. if err != nil {
  288. return errors.Trace(err)
  289. }
  290. tempFile.Close()
  291. config.Logger.WithTraceFields(common.LogFields{
  292. "content": pfConf,
  293. }).Debug("pf.conf")
  294. // Disable first to avoid "pfctl: pf already enabled"
  295. _ = common.RunNetworkConfigCommand(
  296. config.Logger,
  297. config.SudoNetworkConfigCommands,
  298. "pfctl",
  299. "-q",
  300. "-d")
  301. err = common.RunNetworkConfigCommand(
  302. config.Logger,
  303. config.SudoNetworkConfigCommands,
  304. "pfctl",
  305. "-q",
  306. "-e",
  307. "-f", tempFile.Name())
  308. if err != nil {
  309. return errors.Trace(err)
  310. }
  311. return nil
  312. }
  313. func configureClientInterface(
  314. config *ClientConfig,
  315. tunDeviceName string) error {
  316. // TODO: fix or remove the following broken code
  317. return errors.Trace(errUnsupported)
  318. // Set tun device network addresses and MTU
  319. IPv4Address, IPv4Netmask, err := splitIPMask(config.IPv4AddressCIDR)
  320. if err != nil {
  321. return errors.Trace(err)
  322. }
  323. err = common.RunNetworkConfigCommand(
  324. config.Logger,
  325. config.SudoNetworkConfigCommands,
  326. "ifconfig",
  327. tunDeviceName,
  328. IPv4Address, IPv4Address,
  329. "netmask", IPv4Netmask,
  330. "mtu", strconv.Itoa(getMTU(config.MTU)),
  331. "up")
  332. if err != nil {
  333. return errors.Trace(err)
  334. }
  335. IPv6Address, IPv6Prefixlen, err := splitIPPrefixLen(serverIPv6AddressCIDR)
  336. if err != nil {
  337. return errors.Trace(err)
  338. }
  339. err = common.RunNetworkConfigCommand(
  340. config.Logger,
  341. config.SudoNetworkConfigCommands,
  342. "ifconfig",
  343. tunDeviceName,
  344. "inet6", IPv6Address, "prefixlen", IPv6Prefixlen)
  345. if err != nil {
  346. return errors.Trace(err)
  347. }
  348. // Set routing. Routes set here should automatically
  349. // drop when the tun device is removed.
  350. for _, destination := range config.RouteDestinations {
  351. // TODO: IPv6
  352. err = common.RunNetworkConfigCommand(
  353. config.Logger,
  354. config.SudoNetworkConfigCommands,
  355. "route",
  356. "add",
  357. "-ifscope", tunDeviceName,
  358. destination,
  359. IPv4Address)
  360. if err != nil {
  361. return errors.Trace(err)
  362. }
  363. }
  364. return nil
  365. }
  366. // BindToDevice binds a socket to the specified interface.
  367. func BindToDevice(fd int, deviceName string) error {
  368. netInterface, err := net.InterfaceByName(deviceName)
  369. if err != nil {
  370. return errors.Trace(err)
  371. }
  372. // IP_BOUND_IF definition from <netinet/in.h>
  373. const IP_BOUND_IF = 25
  374. err = syscall.SetsockoptInt(fd, syscall.IPPROTO_IP, IP_BOUND_IF, netInterface.Index)
  375. if err != nil {
  376. return errors.Trace(err)
  377. }
  378. return nil
  379. }
  380. func fixBindToDevice(_ common.Logger, _ bool, _ string) error {
  381. // Not required on Darwin
  382. return nil
  383. }