github.com/nicocha30/gvisor-ligolo@v0.0.0-20230726075806-989fa2c0a413/pkg/sentry/socket/netlink/route/protocol.go (about) 1 // Copyright 2018 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 route provides a NETLINK_ROUTE socket protocol. 16 package route 17 18 import ( 19 "bytes" 20 21 "github.com/nicocha30/gvisor-ligolo/pkg/abi/linux" 22 "github.com/nicocha30/gvisor-ligolo/pkg/context" 23 "github.com/nicocha30/gvisor-ligolo/pkg/errors/linuxerr" 24 "github.com/nicocha30/gvisor-ligolo/pkg/marshal/primitive" 25 "github.com/nicocha30/gvisor-ligolo/pkg/sentry/inet" 26 "github.com/nicocha30/gvisor-ligolo/pkg/sentry/kernel" 27 "github.com/nicocha30/gvisor-ligolo/pkg/sentry/kernel/auth" 28 "github.com/nicocha30/gvisor-ligolo/pkg/sentry/socket/netlink" 29 "github.com/nicocha30/gvisor-ligolo/pkg/syserr" 30 ) 31 32 // commandKind describes the operational class of a message type. 33 // 34 // The route message types use the lower 2 bits of the type to describe class 35 // of command. 36 type commandKind int 37 38 const ( 39 kindNew commandKind = 0x0 40 kindDel commandKind = 0x1 41 kindGet commandKind = 0x2 42 kindSet commandKind = 0x3 43 ) 44 45 func typeKind(typ uint16) commandKind { 46 return commandKind(typ & 0x3) 47 } 48 49 // Protocol implements netlink.Protocol. 50 // 51 // +stateify savable 52 type Protocol struct{} 53 54 var _ netlink.Protocol = (*Protocol)(nil) 55 56 // NewProtocol creates a NETLINK_ROUTE netlink.Protocol. 57 func NewProtocol(t *kernel.Task) (netlink.Protocol, *syserr.Error) { 58 return &Protocol{}, nil 59 } 60 61 // Protocol implements netlink.Protocol.Protocol. 62 func (p *Protocol) Protocol() int { 63 return linux.NETLINK_ROUTE 64 } 65 66 // CanSend implements netlink.Protocol.CanSend. 67 func (p *Protocol) CanSend() bool { 68 return true 69 } 70 71 // dumpLinks handles RTM_GETLINK dump requests. 72 func (p *Protocol) dumpLinks(ctx context.Context, msg *netlink.Message, ms *netlink.MessageSet) *syserr.Error { 73 // NLM_F_DUMP + RTM_GETLINK messages are supposed to include an 74 // ifinfomsg. However, Linux <3.9 only checked for rtgenmsg, and some 75 // userspace applications (including glibc) still include rtgenmsg. 76 // Linux has a workaround based on the total message length. 77 // 78 // We don't bother to check for either, since we don't support any 79 // extra attributes that may be included anyways. 80 // 81 // The message may also contain netlink attribute IFLA_EXT_MASK, which 82 // we don't support. 83 84 // The RTM_GETLINK dump response is a set of messages each containing 85 // an InterfaceInfoMessage followed by a set of netlink attributes. 86 87 // We always send back an NLMSG_DONE. 88 ms.Multi = true 89 90 stack := inet.StackFromContext(ctx) 91 if stack == nil { 92 // No network devices. 93 return nil 94 } 95 96 for idx, i := range stack.Interfaces() { 97 addNewLinkMessage(ms, idx, i) 98 } 99 100 return nil 101 } 102 103 // getLinks handles RTM_GETLINK requests. 104 func (p *Protocol) getLink(ctx context.Context, msg *netlink.Message, ms *netlink.MessageSet) *syserr.Error { 105 stack := inet.StackFromContext(ctx) 106 if stack == nil { 107 // No network devices. 108 return nil 109 } 110 111 // Parse message. 112 var ifi linux.InterfaceInfoMessage 113 attrs, ok := msg.GetData(&ifi) 114 if !ok { 115 return syserr.ErrInvalidArgument 116 } 117 118 // Parse attributes. 119 var byName []byte 120 for !attrs.Empty() { 121 ahdr, value, rest, ok := attrs.ParseFirst() 122 if !ok { 123 return syserr.ErrInvalidArgument 124 } 125 attrs = rest 126 127 switch ahdr.Type { 128 case linux.IFLA_IFNAME: 129 if len(value) < 1 { 130 return syserr.ErrInvalidArgument 131 } 132 byName = value[:len(value)-1] 133 134 // TODO(gvisor.dev/issue/578): Support IFLA_EXT_MASK. 135 } 136 } 137 138 found := false 139 for idx, i := range stack.Interfaces() { 140 switch { 141 case ifi.Index > 0: 142 if idx != ifi.Index { 143 continue 144 } 145 case byName != nil: 146 if string(byName) != i.Name { 147 continue 148 } 149 default: 150 // Criteria not specified. 151 return syserr.ErrInvalidArgument 152 } 153 154 addNewLinkMessage(ms, idx, i) 155 found = true 156 break 157 } 158 if !found { 159 return syserr.ErrNoDevice 160 } 161 return nil 162 } 163 164 // delLink handles RTM_DELLINK requests. 165 func (p *Protocol) delLink(ctx context.Context, msg *netlink.Message, ms *netlink.MessageSet) *syserr.Error { 166 stack := inet.StackFromContext(ctx) 167 if stack == nil { 168 // No network stack. 169 return syserr.ErrProtocolNotSupported 170 } 171 172 var ifinfomsg linux.InterfaceInfoMessage 173 attrs, ok := msg.GetData(&ifinfomsg) 174 if !ok { 175 return syserr.ErrInvalidArgument 176 } 177 if ifinfomsg.Index == 0 { 178 // The index is unspecified, search by the interface name. 179 ahdr, value, _, ok := attrs.ParseFirst() 180 if !ok { 181 return syserr.ErrInvalidArgument 182 } 183 switch ahdr.Type { 184 case linux.IFLA_IFNAME: 185 if len(value) < 1 { 186 return syserr.ErrInvalidArgument 187 } 188 ifname := string(value[:len(value)-1]) 189 for idx, ifa := range stack.Interfaces() { 190 if ifname == ifa.Name { 191 ifinfomsg.Index = idx 192 break 193 } 194 } 195 default: 196 return syserr.ErrInvalidArgument 197 } 198 if ifinfomsg.Index == 0 { 199 return syserr.ErrNoDevice 200 } 201 } 202 return syserr.FromError(stack.RemoveInterface(ifinfomsg.Index)) 203 } 204 205 // addNewLinkMessage appends RTM_NEWLINK message for the given interface into 206 // the message set. 207 func addNewLinkMessage(ms *netlink.MessageSet, idx int32, i inet.Interface) { 208 m := ms.AddMessage(linux.NetlinkMessageHeader{ 209 Type: linux.RTM_NEWLINK, 210 }) 211 212 m.Put(&linux.InterfaceInfoMessage{ 213 Family: linux.AF_UNSPEC, 214 Type: i.DeviceType, 215 Index: idx, 216 Flags: i.Flags, 217 }) 218 219 m.PutAttrString(linux.IFLA_IFNAME, i.Name) 220 m.PutAttr(linux.IFLA_MTU, primitive.AllocateUint32(i.MTU)) 221 222 mac := make([]byte, 6) 223 brd := mac 224 if len(i.Addr) > 0 { 225 mac = i.Addr 226 brd = bytes.Repeat([]byte{0xff}, len(i.Addr)) 227 } 228 m.PutAttr(linux.IFLA_ADDRESS, primitive.AsByteSlice(mac)) 229 m.PutAttr(linux.IFLA_BROADCAST, primitive.AsByteSlice(brd)) 230 231 // TODO(gvisor.dev/issue/578): There are many more attributes. 232 } 233 234 // dumpAddrs handles RTM_GETADDR dump requests. 235 func (p *Protocol) dumpAddrs(ctx context.Context, msg *netlink.Message, ms *netlink.MessageSet) *syserr.Error { 236 // RTM_GETADDR dump requests need not contain anything more than the 237 // netlink header and 1 byte protocol family common to all 238 // NETLINK_ROUTE requests. 239 // 240 // TODO(b/68878065): Filter output by passed protocol family. 241 242 // The RTM_GETADDR dump response is a set of RTM_NEWADDR messages each 243 // containing an InterfaceAddrMessage followed by a set of netlink 244 // attributes. 245 246 // We always send back an NLMSG_DONE. 247 ms.Multi = true 248 249 stack := inet.StackFromContext(ctx) 250 if stack == nil { 251 // No network devices. 252 return nil 253 } 254 255 for id, as := range stack.InterfaceAddrs() { 256 for _, a := range as { 257 m := ms.AddMessage(linux.NetlinkMessageHeader{ 258 Type: linux.RTM_NEWADDR, 259 }) 260 261 m.Put(&linux.InterfaceAddrMessage{ 262 Family: a.Family, 263 PrefixLen: a.PrefixLen, 264 Index: uint32(id), 265 }) 266 267 addr := primitive.ByteSlice([]byte(a.Addr)) 268 m.PutAttr(linux.IFA_LOCAL, &addr) 269 m.PutAttr(linux.IFA_ADDRESS, &addr) 270 271 // TODO(gvisor.dev/issue/578): There are many more attributes. 272 } 273 } 274 275 return nil 276 } 277 278 // commonPrefixLen reports the length of the longest IP address prefix. 279 // This is a simplied version from Golang's src/net/addrselect.go. 280 func commonPrefixLen(a, b []byte) (cpl int) { 281 for len(a) > 0 { 282 if a[0] == b[0] { 283 cpl += 8 284 a = a[1:] 285 b = b[1:] 286 continue 287 } 288 bits := 8 289 ab, bb := a[0], b[0] 290 for { 291 ab >>= 1 292 bb >>= 1 293 bits-- 294 if ab == bb { 295 cpl += bits 296 return 297 } 298 } 299 } 300 return 301 } 302 303 // fillRoute returns the Route using LPM algorithm. Refer to Linux's 304 // net/ipv4/route.c:rt_fill_info(). 305 func fillRoute(routes []inet.Route, addr []byte) (inet.Route, *syserr.Error) { 306 family := uint8(linux.AF_INET) 307 if len(addr) != 4 { 308 family = linux.AF_INET6 309 } 310 311 idx := -1 // Index of the Route rule to be returned. 312 idxDef := -1 // Index of the default route rule. 313 prefix := 0 // Current longest prefix. 314 for i, route := range routes { 315 if route.Family != family { 316 continue 317 } 318 319 if len(route.GatewayAddr) > 0 && route.DstLen == 0 { 320 idxDef = i 321 continue 322 } 323 324 cpl := commonPrefixLen(addr, route.DstAddr) 325 if cpl < int(route.DstLen) { 326 continue 327 } 328 cpl = int(route.DstLen) 329 if cpl > prefix { 330 idx = i 331 prefix = cpl 332 } 333 } 334 if idx == -1 { 335 idx = idxDef 336 } 337 if idx == -1 { 338 return inet.Route{}, syserr.ErrHostUnreachable 339 } 340 341 route := routes[idx] 342 if family == linux.AF_INET { 343 route.DstLen = 32 344 } else { 345 route.DstLen = 128 346 } 347 route.DstAddr = addr 348 route.Flags |= linux.RTM_F_CLONED // This route is cloned. 349 return route, nil 350 } 351 352 // parseForDestination parses a message as format of RouteMessage-RtAttr-dst. 353 func parseForDestination(msg *netlink.Message) ([]byte, *syserr.Error) { 354 var rtMsg linux.RouteMessage 355 attrs, ok := msg.GetData(&rtMsg) 356 if !ok { 357 return nil, syserr.ErrInvalidArgument 358 } 359 // iproute2 added the RTM_F_LOOKUP_TABLE flag in version v4.4.0. See 360 // commit bc234301af12. Note we don't check this flag for backward 361 // compatibility. 362 if rtMsg.Flags != 0 && rtMsg.Flags != linux.RTM_F_LOOKUP_TABLE { 363 return nil, syserr.ErrNotSupported 364 } 365 366 // Expect first attribute is RTA_DST. 367 if hdr, value, _, ok := attrs.ParseFirst(); ok && hdr.Type == linux.RTA_DST { 368 return value, nil 369 } 370 return nil, syserr.ErrInvalidArgument 371 } 372 373 // dumpRoutes handles RTM_GETROUTE requests. 374 func (p *Protocol) dumpRoutes(ctx context.Context, msg *netlink.Message, ms *netlink.MessageSet) *syserr.Error { 375 // RTM_GETROUTE dump requests need not contain anything more than the 376 // netlink header and 1 byte protocol family common to all 377 // NETLINK_ROUTE requests. 378 379 stack := inet.StackFromContext(ctx) 380 if stack == nil { 381 // No network routes. 382 return nil 383 } 384 385 hdr := msg.Header() 386 routeTables := stack.RouteTable() 387 388 if hdr.Flags == linux.NLM_F_REQUEST { 389 dst, err := parseForDestination(msg) 390 if err != nil { 391 return err 392 } 393 route, err := fillRoute(routeTables, dst) 394 if err != nil { 395 // TODO(gvisor.dev/issue/1237): return NLMSG_ERROR with ENETUNREACH. 396 return syserr.ErrNotSupported 397 } 398 routeTables = append([]inet.Route{}, route) 399 } else if hdr.Flags&linux.NLM_F_DUMP == linux.NLM_F_DUMP { 400 // We always send back an NLMSG_DONE. 401 ms.Multi = true 402 } else { 403 // TODO(b/68878065): Only above cases are supported. 404 return syserr.ErrNotSupported 405 } 406 407 for _, rt := range routeTables { 408 m := ms.AddMessage(linux.NetlinkMessageHeader{ 409 Type: linux.RTM_NEWROUTE, 410 }) 411 412 m.Put(&linux.RouteMessage{ 413 Family: rt.Family, 414 DstLen: rt.DstLen, 415 SrcLen: rt.SrcLen, 416 TOS: rt.TOS, 417 418 // Always return the main table since we don't have multiple 419 // routing tables. 420 Table: linux.RT_TABLE_MAIN, 421 Protocol: rt.Protocol, 422 Scope: rt.Scope, 423 Type: rt.Type, 424 425 Flags: rt.Flags, 426 }) 427 428 m.PutAttr(254, primitive.AsByteSlice([]byte{123})) 429 if rt.DstLen > 0 { 430 m.PutAttr(linux.RTA_DST, primitive.AsByteSlice(rt.DstAddr)) 431 } 432 if rt.SrcLen > 0 { 433 m.PutAttr(linux.RTA_SRC, primitive.AsByteSlice(rt.SrcAddr)) 434 } 435 if rt.OutputInterface != 0 { 436 m.PutAttr(linux.RTA_OIF, primitive.AllocateInt32(rt.OutputInterface)) 437 } 438 if len(rt.GatewayAddr) > 0 { 439 m.PutAttr(linux.RTA_GATEWAY, primitive.AsByteSlice(rt.GatewayAddr)) 440 } 441 442 // TODO(gvisor.dev/issue/578): There are many more attributes. 443 } 444 445 return nil 446 } 447 448 // newAddr handles RTM_NEWADDR requests. 449 func (p *Protocol) newAddr(ctx context.Context, msg *netlink.Message, ms *netlink.MessageSet) *syserr.Error { 450 stack := inet.StackFromContext(ctx) 451 if stack == nil { 452 // No network stack. 453 return syserr.ErrProtocolNotSupported 454 } 455 456 var ifa linux.InterfaceAddrMessage 457 attrs, ok := msg.GetData(&ifa) 458 if !ok { 459 return syserr.ErrInvalidArgument 460 } 461 462 for !attrs.Empty() { 463 ahdr, value, rest, ok := attrs.ParseFirst() 464 if !ok { 465 return syserr.ErrInvalidArgument 466 } 467 attrs = rest 468 469 // NOTE: A netlink message will contain multiple header attributes. 470 // Both the IFA_ADDRESS and IFA_LOCAL attributes are typically sent 471 // with IFA_ADDRESS being a prefix address and IFA_LOCAL being the 472 // local interface address. We add the local interface address here 473 // and ignore the IFA_ADDRESS. 474 switch ahdr.Type { 475 case linux.IFA_LOCAL: 476 err := stack.AddInterfaceAddr(int32(ifa.Index), inet.InterfaceAddr{ 477 Family: ifa.Family, 478 PrefixLen: ifa.PrefixLen, 479 Flags: ifa.Flags, 480 Addr: value, 481 }) 482 if linuxerr.Equals(linuxerr.EEXIST, err) { 483 flags := msg.Header().Flags 484 if flags&linux.NLM_F_EXCL != 0 { 485 return syserr.ErrExists 486 } 487 } else if err != nil { 488 return syserr.ErrInvalidArgument 489 } 490 case linux.IFA_ADDRESS: 491 default: 492 return syserr.ErrNotSupported 493 } 494 } 495 return nil 496 } 497 498 // delAddr handles RTM_DELADDR requests. 499 func (p *Protocol) delAddr(ctx context.Context, msg *netlink.Message, ms *netlink.MessageSet) *syserr.Error { 500 stack := inet.StackFromContext(ctx) 501 if stack == nil { 502 // No network stack. 503 return syserr.ErrProtocolNotSupported 504 } 505 506 var ifa linux.InterfaceAddrMessage 507 attrs, ok := msg.GetData(&ifa) 508 if !ok { 509 return syserr.ErrInvalidArgument 510 } 511 512 for !attrs.Empty() { 513 ahdr, value, rest, ok := attrs.ParseFirst() 514 if !ok { 515 return syserr.ErrInvalidArgument 516 } 517 attrs = rest 518 519 // NOTE: A netlink message will contain multiple header attributes. 520 // Both the IFA_ADDRESS and IFA_LOCAL attributes are typically sent 521 // with IFA_ADDRESS being a prefix address and IFA_LOCAL being the 522 // local interface address. We use the local interface address to 523 // remove the address and ignore the IFA_ADDRESS. 524 switch ahdr.Type { 525 case linux.IFA_LOCAL: 526 err := stack.RemoveInterfaceAddr(int32(ifa.Index), inet.InterfaceAddr{ 527 Family: ifa.Family, 528 PrefixLen: ifa.PrefixLen, 529 Flags: ifa.Flags, 530 Addr: value, 531 }) 532 if err != nil { 533 return syserr.ErrBadLocalAddress 534 } 535 case linux.IFA_ADDRESS: 536 default: 537 return syserr.ErrNotSupported 538 } 539 } 540 541 return nil 542 } 543 544 // ProcessMessage implements netlink.Protocol.ProcessMessage. 545 func (p *Protocol) ProcessMessage(ctx context.Context, msg *netlink.Message, ms *netlink.MessageSet) *syserr.Error { 546 hdr := msg.Header() 547 548 // All messages start with a 1 byte protocol family. 549 var family primitive.Uint8 550 if _, ok := msg.GetData(&family); !ok { 551 // Linux ignores messages missing the protocol family. See 552 // net/core/rtnetlink.c:rtnetlink_rcv_msg. 553 return nil 554 } 555 556 // Non-GET message types require CAP_NET_ADMIN. 557 if typeKind(hdr.Type) != kindGet { 558 creds := auth.CredentialsFromContext(ctx) 559 if !creds.HasCapability(linux.CAP_NET_ADMIN) { 560 return syserr.ErrPermissionDenied 561 } 562 } 563 564 if hdr.Flags&linux.NLM_F_DUMP == linux.NLM_F_DUMP { 565 // TODO(b/68878065): Only the dump variant of the types below are 566 // supported. 567 switch hdr.Type { 568 case linux.RTM_GETLINK: 569 return p.dumpLinks(ctx, msg, ms) 570 case linux.RTM_GETADDR: 571 return p.dumpAddrs(ctx, msg, ms) 572 case linux.RTM_GETROUTE: 573 return p.dumpRoutes(ctx, msg, ms) 574 default: 575 return syserr.ErrNotSupported 576 } 577 } else if hdr.Flags&linux.NLM_F_REQUEST == linux.NLM_F_REQUEST { 578 switch hdr.Type { 579 case linux.RTM_GETLINK: 580 return p.getLink(ctx, msg, ms) 581 case linux.RTM_DELLINK: 582 return p.delLink(ctx, msg, ms) 583 case linux.RTM_GETROUTE: 584 return p.dumpRoutes(ctx, msg, ms) 585 case linux.RTM_NEWADDR: 586 return p.newAddr(ctx, msg, ms) 587 case linux.RTM_DELADDR: 588 return p.delAddr(ctx, msg, ms) 589 default: 590 return syserr.ErrNotSupported 591 } 592 } 593 return syserr.ErrNotSupported 594 } 595 596 // init registers the NETLINK_ROUTE provider. 597 func init() { 598 netlink.RegisterProvider(linux.NETLINK_ROUTE, NewProtocol) 599 }