tun_darwin.go 11 KB

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