tun_darwin.go 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474
  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. "golang.org/x/sys/unix"
  57. )
  58. const (
  59. DEFAULT_PUBLIC_INTERFACE_NAME = "en0"
  60. )
  61. func IsSupported() bool {
  62. return true
  63. }
  64. func makeDeviceInboundBuffer(MTU int) []byte {
  65. // 4 extra bytes to read a utun packet header
  66. return make([]byte, 4+MTU)
  67. }
  68. func makeDeviceOutboundBuffer(MTU int) []byte {
  69. // 4 extra bytes to write a utun packet header
  70. return make([]byte, 4+MTU)
  71. }
  72. // OpenTunDevice opens a file for performing device I/O with
  73. // either a specified tun device, or a new tun device (when
  74. // name is "").
  75. func OpenTunDevice(name string) (*os.File, string, error) {
  76. // Prevent fork between creating fd and setting CLOEXEC
  77. syscall.ForkLock.RLock()
  78. defer syscall.ForkLock.RUnlock()
  79. unit := uint32(0)
  80. if name != "" {
  81. n, err := fmt.Sscanf(name, "utun%d", &unit)
  82. if err == nil && n != 1 {
  83. err = std_errors.New("failed to scan device name")
  84. }
  85. if err != nil {
  86. return nil, "", errors.Trace(err)
  87. }
  88. }
  89. // Darwin utun code based on:
  90. // https://github.com/songgao/water/blob/70591d249921d075889cc49aaef072987e6b354a/syscalls_darwin.go
  91. // Definitions from <ioctl.h>, <sys/socket.h>, <sys/sys_domain.h>
  92. const (
  93. TUN_CONTROL_NAME = "com.apple.net.utun_control"
  94. CTLIOCGINFO = (0x40000000 | 0x80000000) | ((100 & 0x1fff) << 16) | uint32(byte('N'))<<8 | 3
  95. TUNSIFMODE = (0x80000000) | ((4 & 0x1fff) << 16) | uint32(byte('t'))<<8 | 94
  96. PF_SYSTEM = syscall.AF_SYSTEM
  97. SYSPROTO_CONTROL = 2
  98. AF_SYS_CONTROL = 2
  99. UTUN_OPT_IFNAME = 2
  100. )
  101. fd, err := syscall.Socket(
  102. PF_SYSTEM,
  103. syscall.SOCK_DGRAM,
  104. SYSPROTO_CONTROL)
  105. if err != nil {
  106. return nil, "", errors.Trace(err)
  107. }
  108. // Set CLOEXEC so file descriptor not leaked to network config command subprocesses
  109. unix.CloseOnExec(fd)
  110. err = unix.SetNonblock(fd, true)
  111. if err != nil {
  112. unix.Close(fd)
  113. return nil, "", errors.Trace(err)
  114. }
  115. var tunControlName [96]byte
  116. copy(tunControlName[:], TUN_CONTROL_NAME)
  117. ctlInfo := struct {
  118. ctlID uint32
  119. ctlName [96]byte
  120. }{
  121. 0,
  122. tunControlName,
  123. }
  124. _, _, errno := syscall.Syscall(
  125. syscall.SYS_IOCTL,
  126. uintptr(fd),
  127. uintptr(CTLIOCGINFO),
  128. uintptr(unsafe.Pointer(&ctlInfo)))
  129. if errno != 0 {
  130. return nil, "", errors.Trace(errno)
  131. }
  132. sockaddrCtlSize := 32
  133. sockaddrCtl := struct {
  134. scLen uint8
  135. scFamily uint8
  136. ssSysaddr uint16
  137. scID uint32
  138. scUnit uint32
  139. scReserved [5]uint32
  140. }{
  141. uint8(sockaddrCtlSize),
  142. syscall.AF_SYSTEM,
  143. AF_SYS_CONTROL,
  144. ctlInfo.ctlID,
  145. unit,
  146. [5]uint32{},
  147. }
  148. _, _, errno = syscall.RawSyscall(
  149. syscall.SYS_CONNECT,
  150. uintptr(fd),
  151. uintptr(unsafe.Pointer(&sockaddrCtl)),
  152. uintptr(sockaddrCtlSize))
  153. if errno != 0 {
  154. return nil, "", errors.Trace(errno)
  155. }
  156. ifNameSize := uintptr(16)
  157. ifName := struct {
  158. name [16]byte
  159. }{}
  160. _, _, errno = syscall.Syscall6(
  161. syscall.SYS_GETSOCKOPT,
  162. uintptr(fd),
  163. SYSPROTO_CONTROL,
  164. UTUN_OPT_IFNAME,
  165. uintptr(unsafe.Pointer(&ifName)),
  166. uintptr(unsafe.Pointer(&ifNameSize)),
  167. 0)
  168. if errno != 0 {
  169. return nil, "", errors.Trace(errno)
  170. }
  171. deviceName := string(ifName.name[:ifNameSize-1])
  172. file := os.NewFile(uintptr(fd), deviceName)
  173. return file, deviceName, nil
  174. }
  175. func (device *Device) readTunPacket() (int, int, error) {
  176. // Assumes MTU passed to makeDeviceInboundBuffer is actual MTU and
  177. // so buffer is sufficiently large to always read a complete packet,
  178. // along with the 4 byte utun header.
  179. n, err := device.deviceIO.Read(device.inboundBuffer)
  180. if err != nil {
  181. return 0, 0, errors.Trace(err)
  182. }
  183. if n < 4 {
  184. return 0, 0, errors.TraceNew("missing packet prefix")
  185. }
  186. return 4, n - 4, nil
  187. }
  188. func (device *Device) writeTunPacket(packet []byte) error {
  189. // Note: can't use writev via net.Buffers. os.File isn't
  190. // a net.Conn and can't wrap with net.FileConn due to
  191. // fd type. So writes use an intermediate buffer to add
  192. // the header.
  193. // Assumes:
  194. // - device.outboundBuffer[0..2] will be 0, the zero value
  195. // - packet already validated as 4 or 6
  196. // - max len(packet) won't exceed MTU, prellocated size of
  197. // outboundBuffer.
  198. // Write utun header
  199. if len(packet) > 0 && packet[0]>>4 == 4 {
  200. device.outboundBuffer[3] = syscall.AF_INET
  201. } else { // IPv6
  202. device.outboundBuffer[3] = syscall.AF_INET6
  203. }
  204. copy(device.outboundBuffer[4:], packet)
  205. size := 4 + len(packet)
  206. _, err := device.deviceIO.Write(device.outboundBuffer[:size])
  207. if err != nil {
  208. return errors.Trace(err)
  209. }
  210. return nil
  211. }
  212. func resetNATTables(_ *ServerConfig, _ net.IP) error {
  213. // Not supported on Darwin
  214. // TODO: could use pfctl -K?
  215. return nil
  216. }
  217. func configureServerInterface(
  218. config *ServerConfig,
  219. tunDeviceName string) error {
  220. // TODO: fix or remove the following broken code
  221. return errors.Trace(errUnsupported)
  222. // Set tun device network addresses and MTU
  223. IPv4Address, IPv4Netmask, err := splitIPMask(serverIPv4AddressCIDR)
  224. if err != nil {
  225. return errors.Trace(err)
  226. }
  227. err = common.RunNetworkConfigCommand(
  228. config.Logger,
  229. config.SudoNetworkConfigCommands,
  230. "ifconfig",
  231. tunDeviceName,
  232. IPv4Address, IPv4Address, IPv4Netmask,
  233. "mtu", strconv.Itoa(getMTU(config.MTU)),
  234. "up")
  235. if err != nil {
  236. return errors.Trace(err)
  237. }
  238. IPv6Address, IPv6Prefixlen, err := splitIPPrefixLen(serverIPv6AddressCIDR)
  239. if err != nil {
  240. return errors.Trace(err)
  241. }
  242. err = common.RunNetworkConfigCommand(
  243. config.Logger,
  244. config.SudoNetworkConfigCommands,
  245. "ifconfig",
  246. tunDeviceName,
  247. "inet6", IPv6Address, "prefixlen", IPv6Prefixlen)
  248. if err != nil {
  249. return errors.Trace(err)
  250. }
  251. // NAT tun device to external interface
  252. //
  253. // Uses configuration described here:
  254. // https://discussions.apple.com/thread/5538749
  255. egressInterface := config.EgressInterface
  256. if egressInterface == "" {
  257. egressInterface = DEFAULT_PUBLIC_INTERFACE_NAME
  258. }
  259. err = common.RunNetworkConfigCommand(
  260. config.Logger,
  261. config.SudoNetworkConfigCommands,
  262. "sysctl",
  263. "net.inet.ip.forwarding=1")
  264. if err != nil {
  265. return errors.Trace(err)
  266. }
  267. err = common.RunNetworkConfigCommand(
  268. config.Logger,
  269. config.SudoNetworkConfigCommands,
  270. "sysctl",
  271. "net.inet6.ip6.forwarding=1")
  272. if err != nil {
  273. return errors.Trace(err)
  274. }
  275. // TODO:
  276. // - should use -E and preserve existing pf state?
  277. // - OR should use "-F all" to reset everything?
  278. pfConf := fmt.Sprintf(
  279. "nat on %s from %s to any -> (%s)\n"+
  280. "nat on %s from %s to any -> (%s)\n"+
  281. "pass from %s to any keep state\n"+
  282. "pass from %s to any keep state\n\n",
  283. egressInterface, privateSubnetIPv4.String(), egressInterface,
  284. egressInterface, privateSubnetIPv6.String(), egressInterface,
  285. privateSubnetIPv4.String(),
  286. privateSubnetIPv6.String())
  287. tempFile, err := ioutil.TempFile("", "tun_pf_conf")
  288. if err != nil {
  289. return errors.Trace(err)
  290. }
  291. defer os.Remove(tempFile.Name())
  292. _, err = tempFile.Write([]byte(pfConf))
  293. if err != nil {
  294. return errors.Trace(err)
  295. }
  296. tempFile.Close()
  297. config.Logger.WithTraceFields(common.LogFields{
  298. "content": pfConf,
  299. }).Debug("pf.conf")
  300. // Disable first to avoid "pfctl: pf already enabled"
  301. _ = common.RunNetworkConfigCommand(
  302. config.Logger,
  303. config.SudoNetworkConfigCommands,
  304. "pfctl",
  305. "-q",
  306. "-d")
  307. err = common.RunNetworkConfigCommand(
  308. config.Logger,
  309. config.SudoNetworkConfigCommands,
  310. "pfctl",
  311. "-q",
  312. "-e",
  313. "-f", tempFile.Name())
  314. if err != nil {
  315. return errors.Trace(err)
  316. }
  317. return nil
  318. }
  319. func configureClientInterface(
  320. config *ClientConfig,
  321. tunDeviceName string) error {
  322. // TODO: fix or remove the following broken code
  323. return errors.Trace(errUnsupported)
  324. // Set tun device network addresses and MTU
  325. IPv4Address, IPv4Netmask, err := splitIPMask(config.IPv4AddressCIDR)
  326. if err != nil {
  327. return errors.Trace(err)
  328. }
  329. err = common.RunNetworkConfigCommand(
  330. config.Logger,
  331. config.SudoNetworkConfigCommands,
  332. "ifconfig",
  333. tunDeviceName,
  334. IPv4Address, IPv4Address,
  335. "netmask", IPv4Netmask,
  336. "mtu", strconv.Itoa(getMTU(config.MTU)),
  337. "up")
  338. if err != nil {
  339. return errors.Trace(err)
  340. }
  341. IPv6Address, IPv6Prefixlen, err := splitIPPrefixLen(serverIPv6AddressCIDR)
  342. if err != nil {
  343. return errors.Trace(err)
  344. }
  345. err = common.RunNetworkConfigCommand(
  346. config.Logger,
  347. config.SudoNetworkConfigCommands,
  348. "ifconfig",
  349. tunDeviceName,
  350. "inet6", IPv6Address, "prefixlen", IPv6Prefixlen)
  351. if err != nil {
  352. return errors.Trace(err)
  353. }
  354. // Set routing. Routes set here should automatically
  355. // drop when the tun device is removed.
  356. for _, destination := range config.RouteDestinations {
  357. // TODO: IPv6
  358. err = common.RunNetworkConfigCommand(
  359. config.Logger,
  360. config.SudoNetworkConfigCommands,
  361. "route",
  362. "add",
  363. "-ifscope", tunDeviceName,
  364. destination,
  365. IPv4Address)
  366. if err != nil {
  367. return errors.Trace(err)
  368. }
  369. }
  370. return nil
  371. }
  372. // BindToDevice binds a socket to the specified interface.
  373. func BindToDevice(fd int, deviceName string) error {
  374. netInterface, err := net.InterfaceByName(deviceName)
  375. if err != nil {
  376. return errors.Trace(err)
  377. }
  378. // IP_BOUND_IF definition from <netinet/in.h>
  379. const IP_BOUND_IF = 25
  380. err = syscall.SetsockoptInt(fd, syscall.IPPROTO_IP, IP_BOUND_IF, netInterface.Index)
  381. if err != nil {
  382. return errors.Trace(err)
  383. }
  384. return nil
  385. }
  386. func fixBindToDevice(_ common.Logger, _ bool, _ string) error {
  387. // Not required on Darwin
  388. return nil
  389. }