github.com/psiphon-Labs/psiphon-tunnel-core@v2.0.28+incompatible/psiphon/common/tun/tun_linux.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 package tun 21 22 import ( 23 "fmt" 24 "net" 25 "os" 26 "strconv" 27 "strings" 28 "syscall" 29 "unsafe" 30 31 "github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common" 32 "github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/errors" 33 "golang.org/x/sys/unix" 34 ) 35 36 const ( 37 DEFAULT_PUBLIC_INTERFACE_NAME = "eth0" 38 ) 39 40 func IsSupported() bool { 41 return true 42 } 43 44 func makeDeviceInboundBuffer(MTU int) []byte { 45 return make([]byte, MTU) 46 } 47 48 func makeDeviceOutboundBuffer(MTU int) []byte { 49 // On Linux, no outbound buffer is used 50 return nil 51 } 52 53 // OpenTunDevice opens a file for performing device I/O with 54 // either a specified tun device, or a new tun device (when 55 // name is ""). 56 func OpenTunDevice(name string) (*os.File, string, error) { 57 58 // Prevent fork between creating fd and setting CLOEXEC 59 // TODO: is this still necessary with unix.Open? 60 syscall.ForkLock.RLock() 61 defer syscall.ForkLock.RUnlock() 62 63 // Requires process to run as root or have CAP_NET_ADMIN 64 65 // As explained in https://github.com/golang/go/issues/30426, the fd must 66 // not be added to the Go poller before the following TUNSETIFF ioctl 67 // call. This is achieved by using unix.Open -- which opens a raw fd -- 68 // instead of os.FileOpen, followed by the ioctl and finally os.NewFile 69 // to add the fd to the Go poller. 70 // 71 // Set CLOEXEC so file descriptor not leaked to network config command 72 // subprocesses. 73 74 fileName := "/dev/net/tun" 75 76 fd, err := unix.Open(fileName, os.O_RDWR|syscall.O_CLOEXEC, 0) 77 if err != nil { 78 return nil, "", errors.Trace(err) 79 } 80 81 // This code follows snippets in this thread: 82 // https://groups.google.com/forum/#!msg/golang-nuts/x_c_pZ6p95c/8T0JBZLpTwAJ; 83 84 // Definitions from <linux/if.h>, <linux/if_tun.h> 85 86 // Note: using IFF_NO_PI, so packets have no size/flags header. This does mean 87 // that if the MTU is changed after the tun device is initialized, packets could 88 // be truncated when read. 89 90 const ( 91 IFNAMSIZ = 16 92 IF_REQ_PAD_SIZE = 40 - 18 93 IFF_TUN = 0x0001 94 IFF_NO_PI = 0x1000 95 ) 96 97 var ifName [IFNAMSIZ]byte 98 if name == "" { 99 copy(ifName[:], []byte("tun%d")) 100 } else { 101 copy(ifName[:], []byte(name)) 102 } 103 104 ifReq := struct { 105 name [IFNAMSIZ]byte 106 flags uint16 107 pad [IF_REQ_PAD_SIZE]byte 108 }{ 109 ifName, 110 uint16(IFF_TUN | IFF_NO_PI), 111 [IF_REQ_PAD_SIZE]byte{}, 112 } 113 114 _, _, errno := syscall.Syscall( 115 syscall.SYS_IOCTL, 116 uintptr(fd), 117 uintptr(syscall.TUNSETIFF), 118 uintptr(unsafe.Pointer(&ifReq))) 119 if errno != 0 { 120 unix.Close(fd) 121 return nil, "", errors.Trace(errno) 122 } 123 124 err = unix.SetNonblock(fd, true) 125 if err != nil { 126 unix.Close(fd) 127 return nil, "", errors.Trace(err) 128 } 129 130 file := os.NewFile(uintptr(fd), fileName) 131 132 deviceName := strings.Trim(string(ifReq.name[:]), "\x00") 133 134 return file, deviceName, nil 135 } 136 137 func (device *Device) readTunPacket() (int, int, error) { 138 139 // Assumes MTU passed to makeDeviceInboundBuffer is actual MTU and 140 // so buffer is sufficiently large to always read a complete packet. 141 142 n, err := device.deviceIO.Read(device.inboundBuffer) 143 if err != nil { 144 return 0, 0, errors.Trace(err) 145 } 146 return 0, n, nil 147 } 148 149 func (device *Device) writeTunPacket(packet []byte) error { 150 151 // Doesn't need outboundBuffer since there's no header; write directly to device. 152 153 _, err := device.deviceIO.Write(packet) 154 if err != nil { 155 return errors.Trace(err) 156 } 157 return nil 158 } 159 160 func resetNATTables( 161 config *ServerConfig, 162 IPAddress net.IP) error { 163 164 // Uses the "conntrack" command, which is often not installed by default. 165 166 // conntrack --delete -src-nat --orig-src <address> will clear NAT tables of existing 167 // connections, making it less likely that traffic for a previous client using the 168 // specified address will be forwarded to a new client using this address. This is in 169 // the already unlikely event that there's still in-flight traffic when the address is 170 // recycled. 171 172 err := common.RunNetworkConfigCommand( 173 config.Logger, 174 config.SudoNetworkConfigCommands, 175 "conntrack", 176 "--delete", 177 "--src-nat", 178 "--orig-src", 179 IPAddress.String()) 180 if err != nil { 181 182 // conntrack exits with this error message when there are no flows 183 // to delete, which is not a failure condition. 184 if strings.Contains(err.Error(), "0 flow entries have been deleted") { 185 return nil 186 } 187 188 return errors.Trace(err) 189 } 190 191 return nil 192 } 193 194 func configureServerInterface( 195 config *ServerConfig, 196 tunDeviceName string) error { 197 198 // Set tun device network addresses and MTU 199 200 IPv4Address, IPv4Netmask, err := splitIPMask(serverIPv4AddressCIDR) 201 if err != nil { 202 return errors.Trace(err) 203 } 204 205 err = common.RunNetworkConfigCommand( 206 config.Logger, 207 config.SudoNetworkConfigCommands, 208 "ifconfig", 209 tunDeviceName, 210 IPv4Address, "netmask", IPv4Netmask, 211 "mtu", strconv.Itoa(getMTU(config.MTU)), 212 "up") 213 if err != nil { 214 return errors.Trace(err) 215 } 216 217 err = common.RunNetworkConfigCommand( 218 config.Logger, 219 config.SudoNetworkConfigCommands, 220 "ifconfig", 221 tunDeviceName, 222 "add", serverIPv6AddressCIDR) 223 if err != nil { 224 if config.AllowNoIPv6NetworkConfiguration { 225 config.Logger.WithTraceFields( 226 common.LogFields{ 227 "error": err}).Warning( 228 "assign IPv6 address failed") 229 } else { 230 return errors.Trace(err) 231 } 232 } 233 234 egressInterface := config.EgressInterface 235 if egressInterface == "" { 236 egressInterface = DEFAULT_PUBLIC_INTERFACE_NAME 237 } 238 239 // NAT tun device to external interface 240 241 // TODO: need only set forwarding for specific interfaces? 242 243 err = common.RunNetworkConfigCommand( 244 config.Logger, 245 config.SudoNetworkConfigCommands, 246 "sysctl", 247 "net.ipv4.conf.all.forwarding=1") 248 if err != nil { 249 return errors.Trace(err) 250 } 251 252 err = common.RunNetworkConfigCommand( 253 config.Logger, 254 config.SudoNetworkConfigCommands, 255 "sysctl", 256 "net.ipv6.conf.all.forwarding=1") 257 if err != nil { 258 if config.AllowNoIPv6NetworkConfiguration { 259 config.Logger.WithTraceFields( 260 common.LogFields{ 261 "error": err}).Warning( 262 "allow IPv6 forwarding failed") 263 } else { 264 return errors.Trace(err) 265 } 266 } 267 268 // To avoid duplicates, first try to drop existing rule, then add 269 270 for _, mode := range []string{"-D", "-A"} { 271 272 err = common.RunNetworkConfigCommand( 273 config.Logger, 274 config.SudoNetworkConfigCommands, 275 "iptables", 276 "-t", "nat", 277 mode, "POSTROUTING", 278 "-s", privateSubnetIPv4.String(), 279 "-o", egressInterface, 280 "-j", "MASQUERADE") 281 if mode != "-D" && err != nil { 282 return errors.Trace(err) 283 } 284 285 err = common.RunNetworkConfigCommand( 286 config.Logger, 287 config.SudoNetworkConfigCommands, 288 "ip6tables", 289 "-t", "nat", 290 mode, "POSTROUTING", 291 "-s", privateSubnetIPv6.String(), 292 "-o", egressInterface, 293 "-j", "MASQUERADE") 294 if mode != "-D" && err != nil { 295 if config.AllowNoIPv6NetworkConfiguration { 296 config.Logger.WithTraceFields( 297 common.LogFields{ 298 "error": err}).Warning( 299 "configure IPv6 masquerading failed") 300 } else { 301 return errors.Trace(err) 302 } 303 } 304 } 305 306 return nil 307 } 308 309 func configureClientInterface( 310 config *ClientConfig, 311 tunDeviceName string) error { 312 313 // Set tun device network addresses and MTU 314 315 IPv4Address, IPv4Netmask, err := splitIPMask(config.IPv4AddressCIDR) 316 if err != nil { 317 return errors.Trace(err) 318 } 319 320 err = common.RunNetworkConfigCommand( 321 config.Logger, 322 config.SudoNetworkConfigCommands, 323 "ifconfig", 324 tunDeviceName, 325 IPv4Address, 326 "netmask", IPv4Netmask, 327 "mtu", strconv.Itoa(getMTU(config.MTU)), 328 "up") 329 if err != nil { 330 return errors.Trace(err) 331 } 332 333 err = common.RunNetworkConfigCommand( 334 config.Logger, 335 config.SudoNetworkConfigCommands, 336 "ifconfig", 337 tunDeviceName, 338 "add", config.IPv6AddressCIDR) 339 if err != nil { 340 if config.AllowNoIPv6NetworkConfiguration { 341 config.Logger.WithTraceFields( 342 common.LogFields{ 343 "error": err}).Warning( 344 "assign IPv6 address failed") 345 } else { 346 return errors.Trace(err) 347 } 348 } 349 350 // Set routing. Routes set here should automatically 351 // drop when the tun device is removed. 352 353 // TODO: appear to need explicit routing only for IPv6? 354 355 for _, destination := range config.RouteDestinations { 356 357 // Destination may be host (IP) or network (CIDR) 358 359 IP := net.ParseIP(destination) 360 if IP == nil { 361 var err error 362 IP, _, err = net.ParseCIDR(destination) 363 if err != nil { 364 return errors.Trace(err) 365 } 366 } 367 if IP.To4() != nil { 368 continue 369 } 370 371 // Note: use "replace" instead of "add" as route from 372 // previous run (e.g., tun_test case) may not yet be cleared. 373 374 err = common.RunNetworkConfigCommand( 375 config.Logger, 376 config.SudoNetworkConfigCommands, 377 "ip", 378 "-6", 379 "route", "replace", 380 destination, 381 "dev", tunDeviceName) 382 if err != nil { 383 if config.AllowNoIPv6NetworkConfiguration { 384 config.Logger.WithTraceFields( 385 common.LogFields{ 386 "error": err}).Warning("add IPv6 route failed") 387 } else { 388 return errors.Trace(err) 389 } 390 } 391 } 392 393 return nil 394 } 395 396 // BindToDevice binds a socket to the specified interface. 397 func BindToDevice(fd int, deviceName string) error { 398 err := syscall.BindToDevice(fd, deviceName) 399 if err != nil { 400 return errors.Trace(err) 401 } 402 return nil 403 } 404 405 func fixBindToDevice(logger common.Logger, useSudo bool, tunDeviceName string) error { 406 407 // Fix the problem described here: 408 // https://stackoverflow.com/questions/24011205/cant-perform-tcp-handshake-through-a-nat-between-two-nics-with-so-bindtodevice/ 409 // 410 // > the linux kernel is configured on certain mainstream distributions 411 // > (Ubuntu...) to act as a router and drop packets where the source 412 // > address is suspect in order to prevent spoofing (search "rp_filter" on 413 // > https://www.kernel.org/doc/Documentation/networking/ip-sysctl.txt and 414 // > RFC3704) 415 416 err := common.RunNetworkConfigCommand( 417 logger, 418 useSudo, 419 "sysctl", 420 "net.ipv4.conf.all.accept_local=1") 421 if err != nil { 422 return errors.Trace(err) 423 } 424 425 err = common.RunNetworkConfigCommand( 426 logger, 427 useSudo, 428 "sysctl", 429 "net.ipv4.conf.all.rp_filter=0") 430 if err != nil { 431 return errors.Trace(err) 432 } 433 434 err = common.RunNetworkConfigCommand( 435 logger, 436 useSudo, 437 "sysctl", 438 fmt.Sprintf("net.ipv4.conf.%s.rp_filter=0", tunDeviceName)) 439 if err != nil { 440 return errors.Trace(err) 441 } 442 443 return nil 444 }