gvisor.dev/gvisor@v0.0.0-20240520182842-f9d4d51c7e0f/runsc/sandbox/network_unsafe.go (about) 1 // Copyright 2019 The gVisor Authors. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package sandbox 16 17 import ( 18 "fmt" 19 "os" 20 "unsafe" 21 22 "golang.org/x/sys/unix" 23 "gvisor.dev/gvisor/pkg/abi/linux" 24 ) 25 26 type ethtoolValue struct { 27 cmd uint32 28 val uint32 29 } 30 31 type ifreq struct { 32 ifrName [unix.IFNAMSIZ]byte 33 ifrData *ethtoolValue 34 } 35 36 const ( 37 _ETHTOOL_GGSO = 0x00000023 38 ) 39 40 func isGSOEnabled(fd int, intf string) (bool, error) { 41 val := ethtoolValue{ 42 cmd: _ETHTOOL_GGSO, 43 } 44 45 var name [unix.IFNAMSIZ]byte 46 copy(name[:], []byte(intf)) 47 48 ifr := ifreq{ 49 ifrName: name, 50 ifrData: &val, 51 } 52 53 if _, _, err := unix.Syscall(unix.SYS_IOCTL, uintptr(fd), unix.SIOCETHTOOL, uintptr(unsafe.Pointer(&ifr))); err != 0 { 54 return false, err 55 } 56 57 return val.val != 0, nil 58 } 59 60 func writeNATBlob() (*os.File, error) { 61 // Open a socket to use with iptables. 62 iptSock, err := unix.Socket(unix.AF_INET, unix.SOCK_RAW, unix.IPPROTO_ICMP) 63 if err != nil { 64 return nil, fmt.Errorf("failed to open socket for iptables: %v", err) 65 } 66 defer unix.Close(iptSock) 67 68 // Get the iptables info. 69 var NATName [linux.XT_TABLE_MAXNAMELEN]byte 70 copy(NATName[:], []byte("nat\x00")) 71 natInfo := linux.IPTGetinfo{Name: NATName} 72 natInfoLen := int32(unsafe.Sizeof(linux.IPTGetinfo{})) 73 _, _, errno := unix.Syscall6(unix.SYS_GETSOCKOPT, 74 uintptr(iptSock), 75 unix.SOL_IP, 76 linux.IPT_SO_GET_INFO, 77 uintptr(unsafe.Pointer(&natInfo)), 78 uintptr(unsafe.Pointer(&natInfoLen)), 79 0) 80 if errno != 0 { 81 return nil, fmt.Errorf("failed to call IPT_SO_GET_INFO: %v", err) 82 } 83 84 // Get the iptables entries. 85 entries := linux.IPTGetEntries{Name: NATName, Size: natInfo.Size} 86 entriesBufLen := uint32(unsafe.Sizeof(entries)) + natInfo.Size 87 entriesBuf := make([]byte, entriesBufLen) 88 entries.MarshalUnsafe(entriesBuf[:unsafe.Sizeof(entries)]) 89 _, _, errno = unix.Syscall6(unix.SYS_GETSOCKOPT, 90 uintptr(iptSock), 91 unix.SOL_IP, 92 linux.IPT_SO_GET_ENTRIES, 93 uintptr(unsafe.Pointer(&entriesBuf[0])), 94 uintptr(unsafe.Pointer(&entriesBufLen)), 95 0) 96 if errno != 0 { 97 return nil, fmt.Errorf("failed to call IPT_SO_GET_ENTRIES: %v", errno) 98 } 99 var gotEntries linux.IPTGetEntries 100 gotEntries.UnmarshalUnsafe(entriesBuf[:unsafe.Sizeof(entries)]) 101 102 // Construct an IPTReplace that can be used to set rules. 103 replace := linux.IPTReplace{ 104 Name: NATName, 105 ValidHooks: natInfo.ValidHooks, 106 NumEntries: natInfo.NumEntries, 107 Size: natInfo.Size, 108 HookEntry: natInfo.HookEntry, 109 Underflow: natInfo.Underflow, 110 // We don't implement counters yet. 111 NumCounters: 0, 112 Counters: 0, 113 } 114 115 // Marshal into a blob. 116 replaceBuf := make([]byte, unsafe.Sizeof(replace)+uintptr(natInfo.Size)) 117 replace.MarshalUnsafe(replaceBuf[:unsafe.Sizeof(replace)]) 118 if n := copy(replaceBuf[unsafe.Sizeof(replace):], entriesBuf[unsafe.Sizeof(entries):]); uint32(n) != natInfo.Size { 119 panic(fmt.Sprintf("failed to populate entry table: copied %d bytes, but wanted to copy %d", n, natInfo.Size)) 120 } 121 122 // Write blob to a pipe. 123 reader, writer, err := os.Pipe() 124 if err != nil { 125 return nil, fmt.Errorf("failed to create iptables blob pipe: %v", err) 126 } 127 defer writer.Close() 128 if n, err := writer.Write(replaceBuf); n != len(replaceBuf) || err != nil { 129 return nil, fmt.Errorf("failed to write iptables blob: wrote %d bytes (%d expected) and got error: %v", n, len(replaceBuf), err) 130 } 131 return reader, nil 132 }