github.com/nicocha30/gvisor-ligolo@v0.0.0-20230726075806-989fa2c0a413/pkg/sentry/socket/hostinet/netlink.go (about) 1 // Copyright 2023 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 hostinet 16 17 import ( 18 "bytes" 19 "fmt" 20 "syscall" 21 22 "golang.org/x/sys/unix" 23 "github.com/nicocha30/gvisor-ligolo/pkg/abi/linux" 24 "github.com/nicocha30/gvisor-ligolo/pkg/binary" 25 "github.com/nicocha30/gvisor-ligolo/pkg/errors/linuxerr" 26 "github.com/nicocha30/gvisor-ligolo/pkg/hostarch" 27 "github.com/nicocha30/gvisor-ligolo/pkg/marshal" 28 "github.com/nicocha30/gvisor-ligolo/pkg/marshal/primitive" 29 "github.com/nicocha30/gvisor-ligolo/pkg/sentry/inet" 30 "github.com/nicocha30/gvisor-ligolo/pkg/tcpip" 31 ) 32 33 func getInterfaces() (map[int32]inet.Interface, error) { 34 data, err := syscall.NetlinkRIB(unix.RTM_GETLINK, syscall.AF_UNSPEC) 35 if err != nil { 36 return nil, err 37 } 38 msgs, err := syscall.ParseNetlinkMessage(data) 39 if err != nil { 40 return nil, err 41 } 42 ifs := make(map[int32]inet.Interface, len(msgs)) 43 for _, msg := range msgs { 44 if msg.Header.Type != unix.RTM_NEWLINK { 45 continue 46 } 47 if len(msg.Data) < unix.SizeofIfInfomsg { 48 return nil, fmt.Errorf("RTM_GETLINK returned RTM_NEWLINK message with invalid data length (%d bytes, expected at least %d bytes)", len(msg.Data), unix.SizeofIfInfomsg) 49 } 50 var ifinfo linux.InterfaceInfoMessage 51 ifinfo.UnmarshalUnsafe(msg.Data) 52 inetIF := inet.Interface{ 53 DeviceType: ifinfo.Type, 54 Flags: ifinfo.Flags, 55 } 56 // Not clearly documented: syscall.ParseNetlinkRouteAttr will check the 57 // syscall.NetlinkMessage.Header.Type and skip the struct ifinfomsg 58 // accordingly. 59 attrs, err := syscall.ParseNetlinkRouteAttr(&msg) 60 if err != nil { 61 return nil, fmt.Errorf("RTM_GETLINK returned RTM_NEWLINK message with invalid rtattrs: %v", err) 62 } 63 for _, attr := range attrs { 64 switch attr.Attr.Type { 65 case unix.IFLA_ADDRESS: 66 inetIF.Addr = attr.Value 67 case unix.IFLA_IFNAME: 68 inetIF.Name = string(attr.Value[:len(attr.Value)-1]) 69 } 70 } 71 ifs[ifinfo.Index] = inetIF 72 } 73 return ifs, nil 74 } 75 76 func getInterfaceAddrs() (map[int32][]inet.InterfaceAddr, error) { 77 data, err := syscall.NetlinkRIB(unix.RTM_GETADDR, syscall.AF_UNSPEC) 78 if err != nil { 79 return nil, err 80 } 81 msgs, err := syscall.ParseNetlinkMessage(data) 82 if err != nil { 83 return nil, err 84 } 85 addrs := make(map[int32][]inet.InterfaceAddr, len(msgs)) 86 for _, msg := range msgs { 87 if msg.Header.Type != unix.RTM_NEWADDR { 88 continue 89 } 90 if len(msg.Data) < unix.SizeofIfAddrmsg { 91 return nil, fmt.Errorf("RTM_GETADDR returned RTM_NEWADDR message with invalid data length (%d bytes, expected at least %d bytes)", len(msg.Data), unix.SizeofIfAddrmsg) 92 } 93 var ifaddr linux.InterfaceAddrMessage 94 ifaddr.UnmarshalUnsafe(msg.Data) 95 inetAddr := inet.InterfaceAddr{ 96 Family: ifaddr.Family, 97 PrefixLen: ifaddr.PrefixLen, 98 Flags: ifaddr.Flags, 99 } 100 attrs, err := syscall.ParseNetlinkRouteAttr(&msg) 101 if err != nil { 102 return nil, fmt.Errorf("RTM_GETADDR returned RTM_NEWADDR message with invalid rtattrs: %v", err) 103 } 104 for _, attr := range attrs { 105 switch attr.Attr.Type { 106 case unix.IFA_ADDRESS: 107 inetAddr.Addr = attr.Value 108 } 109 } 110 addrs[int32(ifaddr.Index)] = append(addrs[int32(ifaddr.Index)], inetAddr) 111 112 } 113 return addrs, nil 114 } 115 116 func getRoutes() ([]inet.Route, error) { 117 data, err := syscall.NetlinkRIB(unix.RTM_GETROUTE, syscall.AF_UNSPEC) 118 if err != nil { 119 return nil, err 120 } 121 msgs, err := syscall.ParseNetlinkMessage(data) 122 if err != nil { 123 return nil, err 124 } 125 routes, err := extractRoutes(msgs) 126 if err != nil { 127 return nil, err 128 } 129 130 return routes, nil 131 } 132 133 // extractRoutes populates the given routes slice with the data from the host 134 // route table. 135 func extractRoutes(routeMsgs []syscall.NetlinkMessage) ([]inet.Route, error) { 136 var routes []inet.Route 137 for _, routeMsg := range routeMsgs { 138 if routeMsg.Header.Type != unix.RTM_NEWROUTE { 139 continue 140 } 141 142 var ifRoute linux.RouteMessage 143 ifRoute.UnmarshalUnsafe(routeMsg.Data) 144 inetRoute := inet.Route{ 145 Family: ifRoute.Family, 146 DstLen: ifRoute.DstLen, 147 SrcLen: ifRoute.SrcLen, 148 TOS: ifRoute.TOS, 149 Table: ifRoute.Table, 150 Protocol: ifRoute.Protocol, 151 Scope: ifRoute.Scope, 152 Type: ifRoute.Type, 153 Flags: ifRoute.Flags, 154 } 155 156 // Not clearly documented: syscall.ParseNetlinkRouteAttr will check the 157 // syscall.NetlinkMessage.Header.Type and skip the struct rtmsg 158 // accordingly. 159 attrs, err := syscall.ParseNetlinkRouteAttr(&routeMsg) 160 if err != nil { 161 return nil, fmt.Errorf("RTM_GETROUTE returned RTM_NEWROUTE message with invalid rtattrs: %v", err) 162 } 163 164 for _, attr := range attrs { 165 switch attr.Attr.Type { 166 case unix.RTA_DST: 167 inetRoute.DstAddr = attr.Value 168 case unix.RTA_SRC: 169 inetRoute.SrcAddr = attr.Value 170 case unix.RTA_GATEWAY: 171 inetRoute.GatewayAddr = attr.Value 172 case unix.RTA_OIF: 173 expected := int(binary.Size(inetRoute.OutputInterface)) 174 if len(attr.Value) != expected { 175 return nil, fmt.Errorf("RTM_GETROUTE returned RTM_NEWROUTE message with invalid attribute data length (%d bytes, expected %d bytes)", len(attr.Value), expected) 176 } 177 var outputIF primitive.Int32 178 outputIF.UnmarshalUnsafe(attr.Value) 179 inetRoute.OutputInterface = int32(outputIF) 180 } 181 } 182 183 routes = append(routes, inetRoute) 184 } 185 186 return routes, nil 187 } 188 189 // doNetlinkRouteRequest is a more general form of syscall.NetlinkRIB that 190 // allows sending arbitrary (marshallable) structs to the netlink socket. 191 func doNetlinkRouteRequest(msgs []marshal.Marshallable) error { 192 s, err := unix.Socket(unix.AF_NETLINK, unix.SOCK_RAW|unix.SOCK_CLOEXEC, unix.NETLINK_ROUTE) 193 if err != nil { 194 return err 195 } 196 defer syscall.Close(s) 197 sa := syscall.SockaddrNetlink{Family: unix.AF_NETLINK} 198 if err := syscall.Bind(s, &sa); err != nil { 199 return err 200 } 201 202 b := marshal.MarshalAll(msgs) 203 if err := syscall.Sendto(s, b, 0, &sa); err != nil { 204 return err 205 } 206 207 lsa, err := syscall.Getsockname(s) 208 if err != nil { 209 return err 210 } 211 lsanl, ok := lsa.(*syscall.SockaddrNetlink) 212 if !ok { 213 return linuxerr.EINVAL 214 } 215 rbNew := make([]byte, hostarch.PageSize) 216 done: 217 for { 218 rb := rbNew 219 nr, _, err := syscall.Recvfrom(s, rb, 0) 220 if err != nil { 221 return err 222 } 223 if nr < linux.NetlinkMessageHeaderSize { 224 return linuxerr.EINVAL 225 } 226 rb = rb[:nr] 227 msgs, err := syscall.ParseNetlinkMessage(rb) 228 if err != nil { 229 return err 230 } 231 for _, m := range msgs { 232 if m.Header.Seq != 1 || m.Header.Pid != lsanl.Pid { 233 return linuxerr.EINVAL 234 } 235 if m.Header.Type == linux.NLMSG_DONE { 236 break done 237 } 238 if m.Header.Type == linux.NLMSG_ERROR { 239 errno, err := binary.ReadUint32(bytes.NewReader(m.Data[0:4]), hostarch.ByteOrder) 240 if err != nil { 241 return err 242 } 243 if errno == 0 { 244 break done 245 } 246 return linuxerr.ErrorFromUnix(unix.Errno(-errno)) 247 } 248 } 249 } 250 return nil 251 } 252 253 func removeInterface(idx int32) error { 254 // [ NetlinkMessageHeader | InterfaceInfoMessage ] 255 hdr := linux.NetlinkMessageHeader{ 256 Type: linux.RTM_DELLINK, 257 Flags: linux.NLM_F_REQUEST | linux.NLM_F_ACK, 258 Seq: 1, 259 } 260 infoMsg := linux.InterfaceInfoMessage{ 261 Family: linux.AF_UNSPEC, 262 Index: idx, 263 } 264 265 msgs := []marshal.Marshallable{ 266 &hdr, 267 &infoMsg, 268 } 269 hdr.Length = uint32(marshal.TotalSize(msgs)) 270 return doNetlinkRouteRequest(msgs) 271 } 272 273 func doNetlinkInterfaceRequest(typ, flags uint16, idx uint32, addr inet.InterfaceAddr) error { 274 // [ NetlinkMessageHeader | InterfaceAddrMessage | RtAttr | localAddr | RtAttr | peerAddr ] 275 hdr := linux.NetlinkMessageHeader{ 276 Type: typ, 277 Flags: flags | linux.NLM_F_REQUEST | linux.NLM_F_ACK, 278 Seq: 1, 279 } 280 infoMsg := linux.InterfaceAddrMessage{ 281 Family: addr.Family, 282 Index: idx, 283 PrefixLen: addr.PrefixLen, 284 Flags: addr.Flags, 285 } 286 // Local address. 287 localAddr := tcpip.AddrFromSlice(addr.Addr) 288 if addr.Family == linux.AF_INET { 289 localAddr = localAddr.To4() 290 } 291 rtLocal := linux.RtAttr{ 292 Len: linux.SizeOfRtAttr + uint16(localAddr.Len()), 293 Type: linux.IFA_LOCAL, 294 } 295 localAddrBs := primitive.ByteSlice(localAddr.AsSlice()) 296 // Peer is always the local address for us. 297 rtPeer := linux.RtAttr{ 298 Len: linux.SizeOfRtAttr + uint16(localAddr.Len()), 299 Type: linux.IFA_ADDRESS, 300 } 301 peerAddrBs := primitive.ByteSlice(localAddr.AsSlice()) 302 303 msgs := []marshal.Marshallable{ 304 &hdr, 305 &infoMsg, 306 &rtLocal, 307 &localAddrBs, 308 &rtPeer, 309 &peerAddrBs, 310 } 311 hdr.Length = uint32(marshal.TotalSize(msgs)) 312 return doNetlinkRouteRequest(msgs) 313 } 314 315 func addInterfaceAddr(idx int32, addr inet.InterfaceAddr) error { 316 return doNetlinkInterfaceRequest(linux.RTM_NEWADDR, linux.NLM_F_CREATE, uint32(idx), addr) 317 } 318 319 func removeInterfaceAddr(idx int32, addr inet.InterfaceAddr) error { 320 return doNetlinkInterfaceRequest(linux.RTM_DELADDR, 0, uint32(idx), addr) 321 }