inet.af/netstack@v0.0.0-20220214151720-7585b01ddccf/tcpip/transport/internal/network/endpoint.go (about) 1 // Copyright 2021 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 network provides facilities to support tcpip.Endpoints that operate 16 // at the network layer or above. 17 package network 18 19 import ( 20 "fmt" 21 "sync/atomic" 22 23 "inet.af/netstack/sync" 24 "inet.af/netstack/tcpip" 25 "inet.af/netstack/tcpip/header" 26 "inet.af/netstack/tcpip/stack" 27 "inet.af/netstack/tcpip/transport" 28 ) 29 30 // Endpoint is a datagram-based endpoint. It only supports sending datagrams to 31 // a peer. 32 // 33 // +stateify savable 34 type Endpoint struct { 35 // The following fields must only be set once then never changed. 36 stack *stack.Stack `state:"manual"` 37 ops *tcpip.SocketOptions 38 netProto tcpip.NetworkProtocolNumber 39 transProto tcpip.TransportProtocolNumber 40 41 mu sync.RWMutex `state:"nosave"` 42 // +checklocks:mu 43 wasBound bool 44 // owner is the owner of transmitted packets. 45 // 46 // +checklocks:mu 47 owner tcpip.PacketOwner 48 // +checklocks:mu 49 writeShutdown bool 50 // +checklocks:mu 51 effectiveNetProto tcpip.NetworkProtocolNumber 52 // +checklocks:mu 53 connectedRoute *stack.Route `state:"manual"` 54 // +checklocks:mu 55 multicastMemberships map[multicastMembership]struct{} 56 // TODO(https://gvisor.dev/issue/6389): Use different fields for IPv4/IPv6. 57 // +checklocks:mu 58 ttl uint8 59 // TODO(https://gvisor.dev/issue/6389): Use different fields for IPv4/IPv6. 60 // +checklocks:mu 61 multicastTTL uint8 62 // TODO(https://gvisor.dev/issue/6389): Use different fields for IPv4/IPv6. 63 // +checklocks:mu 64 multicastAddr tcpip.Address 65 // TODO(https://gvisor.dev/issue/6389): Use different fields for IPv4/IPv6. 66 // +checklocks:mu 67 multicastNICID tcpip.NICID 68 // +checklocks:mu 69 ipv4TOS uint8 70 // +checklocks:mu 71 ipv6TClass uint8 72 73 // Lock ordering: mu > infoMu. 74 infoMu sync.RWMutex `state:"nosave"` 75 // info has a dedicated mutex so that we can avoid lock ordering violations 76 // when reading the endpoint's info. If we used mu, we need to guarantee 77 // that any lock taken while mu is held is not held when calling Info() 78 // which is not true as of writing (we hold mu while registering transport 79 // endpoints (taking the transport demuxer lock but we also hold the demuxer 80 // lock when delivering packets/errors to endpoints). 81 // 82 // Writes must be performed through setInfo. 83 // 84 // +checklocks:infoMu 85 info stack.TransportEndpointInfo 86 87 // state holds a transport.DatagramBasedEndpointState. 88 // 89 // state must be accessed with atomics so that we can avoid lock ordering 90 // violations when reading the state. If we used mu, we need to guarantee 91 // that any lock taken while mu is held is not held when calling State() 92 // which is not true as of writing (we hold mu while registering transport 93 // endpoints (taking the transport demuxer lock but we also hold the demuxer 94 // lock when delivering packets/errors to endpoints). 95 // 96 // Writes must be performed through setEndpointState. 97 // 98 // +checkatomics 99 state uint32 100 } 101 102 // +stateify savable 103 type multicastMembership struct { 104 nicID tcpip.NICID 105 multicastAddr tcpip.Address 106 } 107 108 // Init initializes the endpoint. 109 func (e *Endpoint) Init(s *stack.Stack, netProto tcpip.NetworkProtocolNumber, transProto tcpip.TransportProtocolNumber, ops *tcpip.SocketOptions) { 110 e.mu.Lock() 111 memberships := e.multicastMemberships 112 e.mu.Unlock() 113 if memberships != nil { 114 panic(fmt.Sprintf("endpoint is already initialized; got e.multicastMemberships = %#v, want = nil", memberships)) 115 } 116 117 switch netProto { 118 case header.IPv4ProtocolNumber, header.IPv6ProtocolNumber: 119 default: 120 panic(fmt.Sprintf("invalid protocol number = %d", netProto)) 121 } 122 123 *e = Endpoint{ 124 stack: s, 125 ops: ops, 126 netProto: netProto, 127 transProto: transProto, 128 129 info: stack.TransportEndpointInfo{ 130 NetProto: netProto, 131 TransProto: transProto, 132 }, 133 effectiveNetProto: netProto, 134 // Linux defaults to TTL=1. 135 multicastTTL: 1, 136 multicastMemberships: make(map[multicastMembership]struct{}), 137 } 138 139 e.mu.Lock() 140 defer e.mu.Unlock() 141 e.setEndpointState(transport.DatagramEndpointStateInitial) 142 } 143 144 // NetProto returns the network protocol the endpoint was initialized with. 145 func (e *Endpoint) NetProto() tcpip.NetworkProtocolNumber { 146 return e.netProto 147 } 148 149 // setEndpointState sets the state of the endpoint. 150 // 151 // e.mu must be held to synchronize changes to state with the rest of the 152 // endpoint. 153 // 154 // +checklocks:e.mu 155 func (e *Endpoint) setEndpointState(state transport.DatagramEndpointState) { 156 atomic.StoreUint32(&e.state, uint32(state)) 157 } 158 159 // State returns the state of the endpoint. 160 func (e *Endpoint) State() transport.DatagramEndpointState { 161 return transport.DatagramEndpointState(atomic.LoadUint32(&e.state)) 162 } 163 164 // Close cleans the endpoint's resources and leaves the endpoint in a closed 165 // state. 166 func (e *Endpoint) Close() { 167 e.mu.Lock() 168 defer e.mu.Unlock() 169 170 if e.State() == transport.DatagramEndpointStateClosed { 171 return 172 } 173 174 for mem := range e.multicastMemberships { 175 e.stack.LeaveGroup(e.netProto, mem.nicID, mem.multicastAddr) 176 } 177 e.multicastMemberships = nil 178 179 if e.connectedRoute != nil { 180 e.connectedRoute.Release() 181 e.connectedRoute = nil 182 } 183 184 e.setEndpointState(transport.DatagramEndpointStateClosed) 185 } 186 187 // SetOwner sets the owner of transmitted packets. 188 func (e *Endpoint) SetOwner(owner tcpip.PacketOwner) { 189 e.mu.Lock() 190 defer e.mu.Unlock() 191 e.owner = owner 192 } 193 194 func calculateTTL(route *stack.Route, ttl uint8, multicastTTL uint8) uint8 { 195 if header.IsV4MulticastAddress(route.RemoteAddress()) || header.IsV6MulticastAddress(route.RemoteAddress()) { 196 return multicastTTL 197 } 198 199 if ttl == 0 { 200 return route.DefaultTTL() 201 } 202 203 return ttl 204 } 205 206 // WriteContext holds the context for a write. 207 type WriteContext struct { 208 transProto tcpip.TransportProtocolNumber 209 route *stack.Route 210 ttl uint8 211 tos uint8 212 owner tcpip.PacketOwner 213 } 214 215 // Release releases held resources. 216 func (c *WriteContext) Release() { 217 c.route.Release() 218 *c = WriteContext{} 219 } 220 221 // WritePacketInfo is the properties of a packet that may be written. 222 type WritePacketInfo struct { 223 NetProto tcpip.NetworkProtocolNumber 224 LocalAddress, RemoteAddress tcpip.Address 225 MaxHeaderLength uint16 226 RequiresTXTransportChecksum bool 227 } 228 229 // PacketInfo returns the properties of a packet that will be written. 230 func (c *WriteContext) PacketInfo() WritePacketInfo { 231 return WritePacketInfo{ 232 NetProto: c.route.NetProto(), 233 LocalAddress: c.route.LocalAddress(), 234 RemoteAddress: c.route.RemoteAddress(), 235 MaxHeaderLength: c.route.MaxHeaderLength(), 236 RequiresTXTransportChecksum: c.route.RequiresTXTransportChecksum(), 237 } 238 } 239 240 // WritePacket attempts to write the packet. 241 func (c *WriteContext) WritePacket(pkt *stack.PacketBuffer, headerIncluded bool) tcpip.Error { 242 pkt.Owner = c.owner 243 244 if headerIncluded { 245 return c.route.WriteHeaderIncludedPacket(pkt) 246 } 247 248 return c.route.WritePacket(stack.NetworkHeaderParams{ 249 Protocol: c.transProto, 250 TTL: c.ttl, 251 TOS: c.tos, 252 }, pkt) 253 } 254 255 // AcquireContextForWrite acquires a WriteContext. 256 func (e *Endpoint) AcquireContextForWrite(opts tcpip.WriteOptions) (WriteContext, tcpip.Error) { 257 e.mu.RLock() 258 defer e.mu.RUnlock() 259 260 // MSG_MORE is unimplemented. This also means that MSG_EOR is a no-op. 261 if opts.More { 262 return WriteContext{}, &tcpip.ErrInvalidOptionValue{} 263 } 264 265 if e.State() == transport.DatagramEndpointStateClosed { 266 return WriteContext{}, &tcpip.ErrInvalidEndpointState{} 267 } 268 269 if e.writeShutdown { 270 return WriteContext{}, &tcpip.ErrClosedForSend{} 271 } 272 273 route := e.connectedRoute 274 if opts.To == nil { 275 // If the user doesn't specify a destination, they should have 276 // connected to another address. 277 if e.State() != transport.DatagramEndpointStateConnected { 278 return WriteContext{}, &tcpip.ErrDestinationRequired{} 279 } 280 281 route.Acquire() 282 } else { 283 // Reject destination address if it goes through a different 284 // NIC than the endpoint was bound to. 285 nicID := opts.To.NIC 286 if nicID == 0 { 287 nicID = tcpip.NICID(e.ops.GetBindToDevice()) 288 } 289 info := e.Info() 290 if info.BindNICID != 0 { 291 if nicID != 0 && nicID != info.BindNICID { 292 return WriteContext{}, &tcpip.ErrNoRoute{} 293 } 294 295 nicID = info.BindNICID 296 } 297 if nicID == 0 { 298 nicID = info.RegisterNICID 299 } 300 301 dst, netProto, err := e.checkV4Mapped(*opts.To) 302 if err != nil { 303 return WriteContext{}, err 304 } 305 306 route, _, err = e.connectRouteRLocked(nicID, dst, netProto) 307 if err != nil { 308 return WriteContext{}, err 309 } 310 } 311 312 if !e.ops.GetBroadcast() && route.IsOutboundBroadcast() { 313 route.Release() 314 return WriteContext{}, &tcpip.ErrBroadcastDisabled{} 315 } 316 317 var tos uint8 318 switch netProto := route.NetProto(); netProto { 319 case header.IPv4ProtocolNumber: 320 tos = e.ipv4TOS 321 case header.IPv6ProtocolNumber: 322 tos = e.ipv6TClass 323 default: 324 panic(fmt.Sprintf("invalid protocol number = %d", netProto)) 325 } 326 327 return WriteContext{ 328 transProto: e.transProto, 329 route: route, 330 ttl: calculateTTL(route, e.ttl, e.multicastTTL), 331 tos: tos, 332 owner: e.owner, 333 }, nil 334 } 335 336 // Disconnect disconnects the endpoint from its peer. 337 func (e *Endpoint) Disconnect() { 338 e.mu.Lock() 339 defer e.mu.Unlock() 340 341 if e.State() != transport.DatagramEndpointStateConnected { 342 return 343 } 344 345 info := e.Info() 346 // Exclude ephemerally bound endpoints. 347 if e.wasBound { 348 info.ID = stack.TransportEndpointID{ 349 LocalAddress: info.BindAddr, 350 } 351 e.setEndpointState(transport.DatagramEndpointStateBound) 352 } else { 353 info.ID = stack.TransportEndpointID{} 354 e.setEndpointState(transport.DatagramEndpointStateInitial) 355 } 356 e.setInfo(info) 357 358 e.connectedRoute.Release() 359 e.connectedRoute = nil 360 } 361 362 // connectRouteRLocked establishes a route to the specified interface or the 363 // configured multicast interface if no interface is specified and the 364 // specified address is a multicast address. 365 // 366 // +checklocksread:e.mu 367 func (e *Endpoint) connectRouteRLocked(nicID tcpip.NICID, addr tcpip.FullAddress, netProto tcpip.NetworkProtocolNumber) (*stack.Route, tcpip.NICID, tcpip.Error) { 368 localAddr := e.Info().ID.LocalAddress 369 if e.isBroadcastOrMulticast(nicID, netProto, localAddr) { 370 // A packet can only originate from a unicast address (i.e., an interface). 371 localAddr = "" 372 } 373 374 if header.IsV4MulticastAddress(addr.Addr) || header.IsV6MulticastAddress(addr.Addr) { 375 if nicID == 0 { 376 nicID = e.multicastNICID 377 } 378 if localAddr == "" && nicID == 0 { 379 localAddr = e.multicastAddr 380 } 381 } 382 383 // Find a route to the desired destination. 384 r, err := e.stack.FindRoute(nicID, localAddr, addr.Addr, netProto, e.ops.GetMulticastLoop()) 385 if err != nil { 386 return nil, 0, err 387 } 388 return r, nicID, nil 389 } 390 391 // Connect connects the endpoint to the address. 392 func (e *Endpoint) Connect(addr tcpip.FullAddress) tcpip.Error { 393 return e.ConnectAndThen(addr, func(_ tcpip.NetworkProtocolNumber, _, _ stack.TransportEndpointID) tcpip.Error { 394 return nil 395 }) 396 } 397 398 // ConnectAndThen connects the endpoint to the address and then calls the 399 // provided function. 400 // 401 // If the function returns an error, the endpoint's state does not change. The 402 // function will be called with the network protocol used to connect to the peer 403 // and the source and destination addresses that will be used to send traffic to 404 // the peer. 405 func (e *Endpoint) ConnectAndThen(addr tcpip.FullAddress, f func(netProto tcpip.NetworkProtocolNumber, previousID, nextID stack.TransportEndpointID) tcpip.Error) tcpip.Error { 406 addr.Port = 0 407 408 e.mu.Lock() 409 defer e.mu.Unlock() 410 411 info := e.Info() 412 nicID := addr.NIC 413 switch e.State() { 414 case transport.DatagramEndpointStateInitial: 415 case transport.DatagramEndpointStateBound, transport.DatagramEndpointStateConnected: 416 if info.BindNICID == 0 { 417 break 418 } 419 420 if nicID != 0 && nicID != info.BindNICID { 421 return &tcpip.ErrInvalidEndpointState{} 422 } 423 424 nicID = info.BindNICID 425 default: 426 return &tcpip.ErrInvalidEndpointState{} 427 } 428 429 addr, netProto, err := e.checkV4Mapped(addr) 430 if err != nil { 431 return err 432 } 433 434 r, nicID, err := e.connectRouteRLocked(nicID, addr, netProto) 435 if err != nil { 436 return err 437 } 438 439 id := stack.TransportEndpointID{ 440 LocalAddress: info.ID.LocalAddress, 441 RemoteAddress: r.RemoteAddress(), 442 } 443 if e.State() == transport.DatagramEndpointStateInitial { 444 id.LocalAddress = r.LocalAddress() 445 } 446 447 if err := f(r.NetProto(), info.ID, id); err != nil { 448 return err 449 } 450 451 if e.connectedRoute != nil { 452 // If the endpoint was previously connected then release any previous route. 453 e.connectedRoute.Release() 454 } 455 e.connectedRoute = r 456 info.ID = id 457 info.RegisterNICID = nicID 458 e.setInfo(info) 459 e.effectiveNetProto = netProto 460 e.setEndpointState(transport.DatagramEndpointStateConnected) 461 return nil 462 } 463 464 // Shutdown shutsdown the endpoint. 465 func (e *Endpoint) Shutdown() tcpip.Error { 466 e.mu.Lock() 467 defer e.mu.Unlock() 468 469 switch state := e.State(); state { 470 case transport.DatagramEndpointStateInitial, transport.DatagramEndpointStateClosed: 471 return &tcpip.ErrNotConnected{} 472 case transport.DatagramEndpointStateBound, transport.DatagramEndpointStateConnected: 473 e.writeShutdown = true 474 return nil 475 default: 476 panic(fmt.Sprintf("unhandled state = %s", state)) 477 } 478 } 479 480 // checkV4MappedRLocked determines the effective network protocol and converts 481 // addr to its canonical form. 482 func (e *Endpoint) checkV4Mapped(addr tcpip.FullAddress) (tcpip.FullAddress, tcpip.NetworkProtocolNumber, tcpip.Error) { 483 info := e.Info() 484 unwrapped, netProto, err := info.AddrNetProtoLocked(addr, e.ops.GetV6Only()) 485 if err != nil { 486 return tcpip.FullAddress{}, 0, err 487 } 488 return unwrapped, netProto, nil 489 } 490 491 func (e *Endpoint) isBroadcastOrMulticast(nicID tcpip.NICID, netProto tcpip.NetworkProtocolNumber, addr tcpip.Address) bool { 492 return addr == header.IPv4Broadcast || header.IsV4MulticastAddress(addr) || header.IsV6MulticastAddress(addr) || e.stack.IsSubnetBroadcast(nicID, netProto, addr) 493 } 494 495 // Bind binds the endpoint to the address. 496 func (e *Endpoint) Bind(addr tcpip.FullAddress) tcpip.Error { 497 return e.BindAndThen(addr, func(tcpip.NetworkProtocolNumber, tcpip.Address) tcpip.Error { 498 return nil 499 }) 500 } 501 502 // BindAndThen binds the endpoint to the address and then calls the provided 503 // function. 504 // 505 // If the function returns an error, the endpoint's state does not change. The 506 // function will be called with the bound network protocol and address. 507 func (e *Endpoint) BindAndThen(addr tcpip.FullAddress, f func(tcpip.NetworkProtocolNumber, tcpip.Address) tcpip.Error) tcpip.Error { 508 addr.Port = 0 509 510 e.mu.Lock() 511 defer e.mu.Unlock() 512 513 // Don't allow binding once endpoint is not in the initial state 514 // anymore. 515 if e.State() != transport.DatagramEndpointStateInitial { 516 return &tcpip.ErrInvalidEndpointState{} 517 } 518 519 addr, netProto, err := e.checkV4Mapped(addr) 520 if err != nil { 521 return err 522 } 523 524 nicID := addr.NIC 525 if len(addr.Addr) != 0 && !e.isBroadcastOrMulticast(addr.NIC, netProto, addr.Addr) { 526 nicID = e.stack.CheckLocalAddress(nicID, netProto, addr.Addr) 527 if nicID == 0 { 528 return &tcpip.ErrBadLocalAddress{} 529 } 530 } 531 532 if err := f(netProto, addr.Addr); err != nil { 533 return err 534 } 535 536 e.wasBound = true 537 538 info := e.Info() 539 info.ID = stack.TransportEndpointID{ 540 LocalAddress: addr.Addr, 541 } 542 info.BindNICID = addr.NIC 543 info.RegisterNICID = nicID 544 info.BindAddr = addr.Addr 545 e.setInfo(info) 546 e.effectiveNetProto = netProto 547 e.setEndpointState(transport.DatagramEndpointStateBound) 548 return nil 549 } 550 551 // WasBound returns true iff the endpoint was ever bound. 552 func (e *Endpoint) WasBound() bool { 553 e.mu.RLock() 554 defer e.mu.RUnlock() 555 return e.wasBound 556 } 557 558 // GetLocalAddress returns the address that the endpoint is bound to. 559 func (e *Endpoint) GetLocalAddress() tcpip.FullAddress { 560 e.mu.RLock() 561 defer e.mu.RUnlock() 562 563 info := e.Info() 564 addr := info.BindAddr 565 if e.State() == transport.DatagramEndpointStateConnected { 566 addr = e.connectedRoute.LocalAddress() 567 } 568 569 return tcpip.FullAddress{ 570 NIC: info.RegisterNICID, 571 Addr: addr, 572 } 573 } 574 575 // GetRemoteAddress returns the address that the endpoint is connected to. 576 func (e *Endpoint) GetRemoteAddress() (tcpip.FullAddress, bool) { 577 e.mu.RLock() 578 defer e.mu.RUnlock() 579 580 if e.State() != transport.DatagramEndpointStateConnected { 581 return tcpip.FullAddress{}, false 582 } 583 584 return tcpip.FullAddress{ 585 Addr: e.connectedRoute.RemoteAddress(), 586 NIC: e.Info().RegisterNICID, 587 }, true 588 } 589 590 // SetSockOptInt sets the socket option. 591 func (e *Endpoint) SetSockOptInt(opt tcpip.SockOptInt, v int) tcpip.Error { 592 switch opt { 593 case tcpip.MTUDiscoverOption: 594 // Return not supported if the value is not disabling path 595 // MTU discovery. 596 if v != tcpip.PMTUDiscoveryDont { 597 return &tcpip.ErrNotSupported{} 598 } 599 600 case tcpip.MulticastTTLOption: 601 e.mu.Lock() 602 e.multicastTTL = uint8(v) 603 e.mu.Unlock() 604 605 case tcpip.TTLOption: 606 e.mu.Lock() 607 e.ttl = uint8(v) 608 e.mu.Unlock() 609 610 case tcpip.IPv4TOSOption: 611 e.mu.Lock() 612 e.ipv4TOS = uint8(v) 613 e.mu.Unlock() 614 615 case tcpip.IPv6TrafficClassOption: 616 e.mu.Lock() 617 e.ipv6TClass = uint8(v) 618 e.mu.Unlock() 619 } 620 621 return nil 622 } 623 624 // GetSockOptInt returns the socket option. 625 func (e *Endpoint) GetSockOptInt(opt tcpip.SockOptInt) (int, tcpip.Error) { 626 switch opt { 627 case tcpip.MTUDiscoverOption: 628 // The only supported setting is path MTU discovery disabled. 629 return tcpip.PMTUDiscoveryDont, nil 630 631 case tcpip.MulticastTTLOption: 632 e.mu.Lock() 633 v := int(e.multicastTTL) 634 e.mu.Unlock() 635 return v, nil 636 637 case tcpip.TTLOption: 638 e.mu.Lock() 639 v := int(e.ttl) 640 e.mu.Unlock() 641 return v, nil 642 643 case tcpip.IPv4TOSOption: 644 e.mu.RLock() 645 v := int(e.ipv4TOS) 646 e.mu.RUnlock() 647 return v, nil 648 649 case tcpip.IPv6TrafficClassOption: 650 e.mu.RLock() 651 v := int(e.ipv6TClass) 652 e.mu.RUnlock() 653 return v, nil 654 655 default: 656 return -1, &tcpip.ErrUnknownProtocolOption{} 657 } 658 } 659 660 // SetSockOpt sets the socket option. 661 func (e *Endpoint) SetSockOpt(opt tcpip.SettableSocketOption) tcpip.Error { 662 switch v := opt.(type) { 663 case *tcpip.MulticastInterfaceOption: 664 e.mu.Lock() 665 defer e.mu.Unlock() 666 667 fa := tcpip.FullAddress{Addr: v.InterfaceAddr} 668 fa, netProto, err := e.checkV4Mapped(fa) 669 if err != nil { 670 return err 671 } 672 nic := v.NIC 673 addr := fa.Addr 674 675 if nic == 0 && addr == "" { 676 e.multicastAddr = "" 677 e.multicastNICID = 0 678 break 679 } 680 681 if nic != 0 { 682 if !e.stack.CheckNIC(nic) { 683 return &tcpip.ErrBadLocalAddress{} 684 } 685 } else { 686 nic = e.stack.CheckLocalAddress(0, netProto, addr) 687 if nic == 0 { 688 return &tcpip.ErrBadLocalAddress{} 689 } 690 } 691 692 if info := e.Info(); info.BindNICID != 0 && info.BindNICID != nic { 693 return &tcpip.ErrInvalidEndpointState{} 694 } 695 696 e.multicastNICID = nic 697 e.multicastAddr = addr 698 699 case *tcpip.AddMembershipOption: 700 if !header.IsV4MulticastAddress(v.MulticastAddr) && !header.IsV6MulticastAddress(v.MulticastAddr) { 701 return &tcpip.ErrInvalidOptionValue{} 702 } 703 704 nicID := v.NIC 705 706 if v.InterfaceAddr.Unspecified() { 707 if nicID == 0 { 708 if r, err := e.stack.FindRoute(0, "", v.MulticastAddr, e.netProto, false /* multicastLoop */); err == nil { 709 nicID = r.NICID() 710 r.Release() 711 } 712 } 713 } else { 714 nicID = e.stack.CheckLocalAddress(nicID, e.netProto, v.InterfaceAddr) 715 } 716 if nicID == 0 { 717 return &tcpip.ErrUnknownDevice{} 718 } 719 720 memToInsert := multicastMembership{nicID: nicID, multicastAddr: v.MulticastAddr} 721 722 e.mu.Lock() 723 defer e.mu.Unlock() 724 725 if _, ok := e.multicastMemberships[memToInsert]; ok { 726 return &tcpip.ErrPortInUse{} 727 } 728 729 if err := e.stack.JoinGroup(e.netProto, nicID, v.MulticastAddr); err != nil { 730 return err 731 } 732 733 e.multicastMemberships[memToInsert] = struct{}{} 734 735 case *tcpip.RemoveMembershipOption: 736 if !header.IsV4MulticastAddress(v.MulticastAddr) && !header.IsV6MulticastAddress(v.MulticastAddr) { 737 return &tcpip.ErrInvalidOptionValue{} 738 } 739 740 nicID := v.NIC 741 if v.InterfaceAddr.Unspecified() { 742 if nicID == 0 { 743 if r, err := e.stack.FindRoute(0, "", v.MulticastAddr, e.netProto, false /* multicastLoop */); err == nil { 744 nicID = r.NICID() 745 r.Release() 746 } 747 } 748 } else { 749 nicID = e.stack.CheckLocalAddress(nicID, e.netProto, v.InterfaceAddr) 750 } 751 if nicID == 0 { 752 return &tcpip.ErrUnknownDevice{} 753 } 754 755 memToRemove := multicastMembership{nicID: nicID, multicastAddr: v.MulticastAddr} 756 757 e.mu.Lock() 758 defer e.mu.Unlock() 759 760 if _, ok := e.multicastMemberships[memToRemove]; !ok { 761 return &tcpip.ErrBadLocalAddress{} 762 } 763 764 if err := e.stack.LeaveGroup(e.netProto, nicID, v.MulticastAddr); err != nil { 765 return err 766 } 767 768 delete(e.multicastMemberships, memToRemove) 769 770 case *tcpip.SocketDetachFilterOption: 771 return nil 772 } 773 return nil 774 } 775 776 // GetSockOpt returns the socket option. 777 func (e *Endpoint) GetSockOpt(opt tcpip.GettableSocketOption) tcpip.Error { 778 switch o := opt.(type) { 779 case *tcpip.MulticastInterfaceOption: 780 e.mu.Lock() 781 *o = tcpip.MulticastInterfaceOption{ 782 NIC: e.multicastNICID, 783 InterfaceAddr: e.multicastAddr, 784 } 785 e.mu.Unlock() 786 787 default: 788 return &tcpip.ErrUnknownProtocolOption{} 789 } 790 return nil 791 } 792 793 // Info returns a copy of the endpoint info. 794 func (e *Endpoint) Info() stack.TransportEndpointInfo { 795 e.infoMu.RLock() 796 defer e.infoMu.RUnlock() 797 return e.info 798 } 799 800 // setInfo sets the endpoint's info. 801 // 802 // e.mu must be held to synchronize changes to info with the rest of the 803 // endpoint. 804 // 805 // +checklocks:e.mu 806 func (e *Endpoint) setInfo(info stack.TransportEndpointInfo) { 807 e.infoMu.Lock() 808 defer e.infoMu.Unlock() 809 e.info = info 810 }