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