| // Copyright 2016 The Netstack Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| // Package stack provides the glue between networking protocols and the |
| // consumers of the networking stack. |
| // |
| // For consumers, the only function of interest is New(), everything else is |
| // provided by the tcpip/public package. |
| // |
| // For protocol implementers, RegisterTransportProtocolFactory() and |
| // RegisterNetworkProtocolFactory() are used to register protocol factories with |
| // the stack, which will then be used to instantiate protocol objects when |
| // consumers interact with the stack. |
| package stack |
| |
| import ( |
| "sync" |
| "sync/atomic" |
| "time" |
| |
| "github.com/google/netstack/tcpip" |
| "github.com/google/netstack/tcpip/buffer" |
| "github.com/google/netstack/tcpip/ports" |
| "github.com/google/netstack/waiter" |
| ) |
| |
| type transportProtocolState struct { |
| proto TransportProtocol |
| defaultHandler func(*Route, TransportEndpointID, *buffer.VectorisedView) bool |
| } |
| |
| // Stack is a networking stack, with all supported protocols, NICs, and route |
| // table. |
| type Stack struct { |
| transportProtocols map[tcpip.TransportProtocolNumber]*transportProtocolState |
| networkProtocols map[tcpip.NetworkProtocolNumber]NetworkProtocol |
| linkAddrResolvers map[tcpip.NetworkProtocolNumber]LinkAddressResolver |
| |
| demux *transportDemuxer |
| |
| stats tcpip.Stats |
| |
| linkAddrCache *linkAddrCache |
| |
| mu sync.RWMutex |
| nics map[tcpip.NICID]*NIC |
| |
| // route is the route table passed in by the user via SetRouteTable(), |
| // it is used by FindRoute() to build a route for a specific |
| // destination. |
| routeTable []tcpip.Route |
| |
| *ports.PortManager |
| } |
| |
| // New allocates a new networking stack with only the requested networking and |
| // transport protocols configured with default options. |
| // |
| // Protocol options can be changed by calling the |
| // SetNetworkProtocolOption/SetTransportProtocolOption methods provided by the |
| // stack. Please refer to individual protocol implementations as to what options |
| // are supported. |
| func New(network []string, transport []string) *Stack { |
| |
| s := &Stack{ |
| transportProtocols: make(map[tcpip.TransportProtocolNumber]*transportProtocolState), |
| networkProtocols: make(map[tcpip.NetworkProtocolNumber]NetworkProtocol), |
| linkAddrResolvers: make(map[tcpip.NetworkProtocolNumber]LinkAddressResolver), |
| nics: make(map[tcpip.NICID]*NIC), |
| linkAddrCache: newLinkAddrCache(1 * time.Minute), |
| PortManager: ports.NewPortManager(), |
| } |
| |
| // Add specified network protocols. |
| for _, name := range network { |
| netProtoFactory, ok := networkProtocols[name] |
| if !ok { |
| continue |
| } |
| netProto := netProtoFactory() |
| s.networkProtocols[netProto.Number()] = netProto |
| if r, ok := netProto.(LinkAddressResolver); ok { |
| s.linkAddrResolvers[r.LinkAddressProtocol()] = r |
| } |
| } |
| |
| // Add specified transport protocols. |
| for _, name := range transport { |
| transProtoFactory, ok := transportProtocols[name] |
| if !ok { |
| continue |
| } |
| transProto := transProtoFactory() |
| s.transportProtocols[transProto.Number()] = &transportProtocolState{ |
| proto: transProto, |
| } |
| } |
| |
| // Create the global transport demuxer. |
| s.demux = newTransportDemuxer(s) |
| |
| return s |
| } |
| |
| // SetNetworkProtocolOption allows configuring individual protocol level |
| // options. This method returns an error if the protocol is not supported or |
| // option is not supported by the protocol implementation or the provided value |
| // is incorrect. |
| func (s *Stack) SetNetworkProtocolOption(network tcpip.NetworkProtocolNumber, option interface{}) *tcpip.Error { |
| netProto, ok := s.networkProtocols[network] |
| if !ok { |
| return tcpip.ErrUnknownProtocol |
| } |
| return netProto.SetOption(option) |
| } |
| |
| // SetTransportProtocolOption allows configuring individual protocol level |
| // options. This method returns an error if the protocol is not supported or |
| // option is not supported by the protocol implementation or the provided value |
| // is incorrect. |
| func (s *Stack) SetTransportProtocolOption(transport tcpip.TransportProtocolNumber, option interface{}) *tcpip.Error { |
| transProtoState, ok := s.transportProtocols[transport] |
| if !ok { |
| return tcpip.ErrUnknownProtocol |
| } |
| return transProtoState.proto.SetOption(option) |
| } |
| |
| // SetTransportProtocolHandler sets the per-stack default handler for the given |
| // protocol. |
| // |
| // It must be called only during initialization of the stack. Changing it as the |
| // stack is operating is not supported. |
| func (s *Stack) SetTransportProtocolHandler(p tcpip.TransportProtocolNumber, h func(*Route, TransportEndpointID, *buffer.VectorisedView) bool) { |
| state := s.transportProtocols[p] |
| if state != nil { |
| state.defaultHandler = h |
| } |
| } |
| |
| // Stats returns a snapshot of the current stats. |
| // |
| // NOTE: The underlying stats are updated using atomic instructions as a result |
| // the snapshot returned does not represent the value of all the stats at any |
| // single given point of time. |
| // TODO: Make stats available in sentry for debugging/diag. |
| func (s *Stack) Stats() tcpip.Stats { |
| return tcpip.Stats{ |
| UnknownProtocolRcvdPackets: atomic.LoadUint64(&s.stats.UnknownProtocolRcvdPackets), |
| UnknownNetworkEndpointRcvdPackets: atomic.LoadUint64(&s.stats.UnknownNetworkEndpointRcvdPackets), |
| MalformedRcvdPackets: atomic.LoadUint64(&s.stats.MalformedRcvdPackets), |
| DroppedPackets: atomic.LoadUint64(&s.stats.DroppedPackets), |
| TCP: tcpip.TCPStats{ |
| ActiveConnectionOpenings: atomic.LoadUint64(&s.stats.TCP.ActiveConnectionOpenings), |
| PassiveConnectionOpenings: atomic.LoadUint64(&s.stats.TCP.PassiveConnectionOpenings), |
| FailedConnectionAttempts: atomic.LoadUint64(&s.stats.TCP.FailedConnectionAttempts), |
| ValidSegmentsReceived: atomic.LoadUint64(&s.stats.TCP.ValidSegmentsReceived), |
| InvalidSegmentsReceived: atomic.LoadUint64(&s.stats.TCP.InvalidSegmentsReceived), |
| SegmentsSent: atomic.LoadUint64(&s.stats.TCP.SegmentsSent), |
| ResetsSent: atomic.LoadUint64(&s.stats.TCP.ResetsSent), |
| }, |
| } |
| } |
| |
| // MutableStats returns a mutable copy of the current stats. |
| // |
| // This is not generally exported via the public interface, but is available |
| // internally. |
| func (s *Stack) MutableStats() *tcpip.Stats { |
| return &s.stats |
| } |
| |
| // SetRouteTable assigns the route table to be used by this stack. It |
| // specifies which NIC to use for given destination address ranges. |
| func (s *Stack) SetRouteTable(table []tcpip.Route) { |
| s.mu.Lock() |
| defer s.mu.Unlock() |
| |
| s.routeTable = table |
| } |
| |
| // GetRouteTable returns the route table which is currently in use. |
| func (s *Stack) GetRouteTable() []tcpip.Route { |
| s.mu.Lock() |
| defer s.mu.Unlock() |
| return append([]tcpip.Route(nil), s.routeTable...) |
| } |
| |
| // NewEndpoint creates a new transport layer endpoint of the given protocol. |
| func (s *Stack) NewEndpoint(transport tcpip.TransportProtocolNumber, network tcpip.NetworkProtocolNumber, waiterQueue *waiter.Queue) (tcpip.Endpoint, *tcpip.Error) { |
| t, ok := s.transportProtocols[transport] |
| if !ok { |
| return nil, tcpip.ErrUnknownProtocol |
| } |
| |
| return t.proto.NewEndpoint(s, network, waiterQueue) |
| } |
| |
| // createNIC creates a NIC with the provided id and link-layer endpoint, and |
| // optionally enable it. |
| func (s *Stack) createNIC(id tcpip.NICID, linkEP tcpip.LinkEndpointID, enabled bool) *tcpip.Error { |
| ep := FindLinkEndpoint(linkEP) |
| if ep == nil { |
| return tcpip.ErrBadLinkEndpoint |
| } |
| |
| s.mu.Lock() |
| defer s.mu.Unlock() |
| |
| // Make sure id is unique. |
| if _, ok := s.nics[id]; ok { |
| return tcpip.ErrDuplicateNICID |
| } |
| |
| n := newNIC(s, id, ep) |
| |
| s.nics[id] = n |
| if enabled { |
| n.attachLinkEndpoint() |
| } |
| |
| return nil |
| } |
| |
| // CreateNIC creates a NIC with the provided id and link-layer endpoint. |
| func (s *Stack) CreateNIC(id tcpip.NICID, linkEP tcpip.LinkEndpointID) *tcpip.Error { |
| return s.createNIC(id, linkEP, true) |
| } |
| |
| // CreateDisabledNIC creates a NIC with the provided id and link-layer endpoint, |
| // but leave it disable. Stack.EnableNIC must be called before the link-layer |
| // endpoint starts delivering packets to it. |
| func (s *Stack) CreateDisabledNIC(id tcpip.NICID, linkEP tcpip.LinkEndpointID) *tcpip.Error { |
| return s.createNIC(id, linkEP, false) |
| } |
| |
| // EnableNIC enables the given NIC so that the link-layer endpoint can start |
| // delivering packets to it. |
| func (s *Stack) EnableNIC(id tcpip.NICID) *tcpip.Error { |
| s.mu.RLock() |
| defer s.mu.RUnlock() |
| |
| nic := s.nics[id] |
| if nic == nil { |
| return tcpip.ErrUnknownNICID |
| } |
| |
| nic.attachLinkEndpoint() |
| |
| return nil |
| } |
| |
| // NICSubnets returns a map of NICIDs to their associated subnets. |
| func (s *Stack) NICSubnets() map[tcpip.NICID][]tcpip.Subnet { |
| s.mu.RLock() |
| defer s.mu.RUnlock() |
| |
| nics := map[tcpip.NICID][]tcpip.Subnet{} |
| |
| for id, nic := range s.nics { |
| nics[id] = append(nics[id], nic.Subnets()...) |
| } |
| return nics |
| } |
| |
| // AddAddress adds a new network-layer address to the specified NIC. |
| func (s *Stack) AddAddress(id tcpip.NICID, protocol tcpip.NetworkProtocolNumber, addr tcpip.Address) *tcpip.Error { |
| return s.AddAddressWithOptions(id, protocol, addr, CanBePrimaryEndpoint) |
| } |
| |
| // AddAddressWithOptions is the same as AddAddress, but allows you to specify whether they new endpoint can be primary or not. |
| func (s *Stack) AddAddressWithOptions(id tcpip.NICID, protocol tcpip.NetworkProtocolNumber, addr tcpip.Address, peb PrimaryEndpointBehavior) *tcpip.Error { |
| s.mu.RLock() |
| defer s.mu.RUnlock() |
| |
| nic := s.nics[id] |
| if nic == nil { |
| return tcpip.ErrUnknownNICID |
| } |
| |
| return nic.AddAddressWithOptions(protocol, addr, peb) |
| } |
| |
| // AddSubnet adds a subnet range to the specified NIC. |
| func (s *Stack) AddSubnet(id tcpip.NICID, protocol tcpip.NetworkProtocolNumber, subnet tcpip.Subnet) *tcpip.Error { |
| s.mu.RLock() |
| defer s.mu.RUnlock() |
| |
| nic := s.nics[id] |
| if nic == nil { |
| return tcpip.ErrUnknownNICID |
| } |
| |
| nic.AddSubnet(protocol, subnet) |
| return nil |
| } |
| |
| // RemoveAddress removes an existing network-layer address from the specified |
| // NIC. |
| func (s *Stack) RemoveAddress(id tcpip.NICID, addr tcpip.Address) *tcpip.Error { |
| s.mu.RLock() |
| defer s.mu.RUnlock() |
| |
| nic := s.nics[id] |
| if nic == nil { |
| return tcpip.ErrUnknownNICID |
| } |
| |
| return nic.RemoveAddress(addr) |
| } |
| |
| // FindRoute creates a route to the given destination address, leaving through |
| // the given nic and local address (if provided). |
| func (s *Stack) FindRoute(id tcpip.NICID, localAddr, remoteAddr tcpip.Address, netProto tcpip.NetworkProtocolNumber) (Route, *tcpip.Error) { |
| s.mu.RLock() |
| defer s.mu.RUnlock() |
| |
| for i := range s.routeTable { |
| if id != 0 && id != s.routeTable[i].NIC || !s.routeTable[i].Match(remoteAddr) { |
| continue |
| } |
| |
| nic := s.nics[s.routeTable[i].NIC] |
| if nic == nil { |
| continue |
| } |
| |
| var ref *referencedNetworkEndpoint |
| if len(localAddr) != 0 { |
| ref = nic.findEndpoint(localAddr) |
| } else { |
| ref = nic.primaryEndpoint(netProto) |
| } |
| |
| if ref == nil { |
| continue |
| } |
| |
| r := makeRoute(netProto, ref.ep.ID().LocalAddress, remoteAddr, ref) |
| r.NextHop = s.routeTable[i].Gateway |
| return r, nil |
| } |
| |
| return Route{}, tcpip.ErrNoRoute |
| } |
| |
| // CheckNetworkProtocol checks if a given network protocol is enabled in the |
| // stack. |
| func (s *Stack) CheckNetworkProtocol(protocol tcpip.NetworkProtocolNumber) bool { |
| _, ok := s.networkProtocols[protocol] |
| return ok |
| } |
| |
| // CheckLocalAddress determines if the given local address exists, and if it |
| // does, returns the id of the NIC it's bound to. Returns 0 if the address |
| // does not exist. |
| func (s *Stack) CheckLocalAddress(nicid tcpip.NICID, addr tcpip.Address) tcpip.NICID { |
| s.mu.RLock() |
| defer s.mu.RUnlock() |
| |
| // If a NIC is specified, we try to find the address there only. |
| if nicid != 0 { |
| nic := s.nics[nicid] |
| if nic == nil { |
| return 0 |
| } |
| |
| ref := nic.findEndpoint(addr) |
| if ref == nil { |
| return 0 |
| } |
| |
| ref.decRef() |
| |
| return nic.id |
| } |
| |
| // Go through all the NICs. |
| for _, nic := range s.nics { |
| ref := nic.findEndpoint(addr) |
| if ref != nil { |
| ref.decRef() |
| return nic.id |
| } |
| } |
| |
| return 0 |
| } |
| |
| // SetPromiscuousMode enables or disables promiscuous mode in the given NIC. |
| func (s *Stack) SetPromiscuousMode(nicID tcpip.NICID, enable bool) *tcpip.Error { |
| s.mu.RLock() |
| defer s.mu.RUnlock() |
| |
| nic := s.nics[nicID] |
| if nic == nil { |
| return tcpip.ErrUnknownNICID |
| } |
| |
| nic.setPromiscuousMode(enable) |
| |
| return nil |
| } |
| |
| // AddLinkAddress adds a link address to the stack link cache. |
| func (s *Stack) AddLinkAddress(nicid tcpip.NICID, addr tcpip.Address, linkAddr tcpip.LinkAddress) { |
| fullAddr := tcpip.FullAddress{NIC: nicid, Addr: addr} |
| s.linkAddrCache.add(fullAddr, linkAddr) |
| // TODO(crawshaw): provide a way for a |
| // transport endpoint to receive a signal that AddLinkAddress |
| // for a particular address has been called. |
| } |
| |
| func (s *Stack) GetLinkAddress(nicid tcpip.NICID, addr tcpip.Address, timeout time.Duration) tcpip.LinkAddress { |
| if addr == "\xff\xff\xff\xff" { |
| return "\xff\xff\xff\xff\xff\xff" |
| } |
| fullAddr := tcpip.FullAddress{NIC: nicid, Addr: addr} |
| return s.linkAddrCache.get(fullAddr, timeout) |
| } |
| |
| // RegisterTransportEndpoint registers the given endpoint with the stack |
| // transport dispatcher. Received packets that match the provided id will be |
| // delivered to the given endpoint; specifying a nic is optional, but |
| // nic-specific IDs have precedence over global ones. |
| func (s *Stack) RegisterTransportEndpoint(nicID tcpip.NICID, netProtos []tcpip.NetworkProtocolNumber, protocol tcpip.TransportProtocolNumber, id TransportEndpointID, ep TransportEndpoint) *tcpip.Error { |
| if nicID == 0 { |
| return s.demux.registerEndpoint(netProtos, protocol, id, ep) |
| } |
| |
| s.mu.RLock() |
| defer s.mu.RUnlock() |
| |
| nic := s.nics[nicID] |
| if nic == nil { |
| return tcpip.ErrUnknownNICID |
| } |
| |
| return nic.demux.registerEndpoint(netProtos, protocol, id, ep) |
| } |
| |
| // UnregisterTransportEndpoint removes the endpoint with the given id from the |
| // stack transport dispatcher. |
| func (s *Stack) UnregisterTransportEndpoint(nicID tcpip.NICID, netProtos []tcpip.NetworkProtocolNumber, protocol tcpip.TransportProtocolNumber, id TransportEndpointID) { |
| if nicID == 0 { |
| s.demux.unregisterEndpoint(netProtos, protocol, id) |
| return |
| } |
| |
| s.mu.RLock() |
| defer s.mu.RUnlock() |
| |
| nic := s.nics[nicID] |
| if nic != nil { |
| nic.demux.unregisterEndpoint(netProtos, protocol, id) |
| } |
| } |
| |
| // JoinGroup joins the given multicast group on every interface that |
| // matches the given interface address. |
| // TODO: notify network of subscription via igmp protocol |
| func (s *Stack) JoinGroup(protocol tcpip.NetworkProtocolNumber, interfaceAddr tcpip.Address, multicastAddr tcpip.Address) *tcpip.Error { |
| s.mu.RLock() |
| defer s.mu.RUnlock() |
| |
| epFound := false |
| for _, n := range s.nics { |
| ref := n.findEndpoint(interfaceAddr) |
| if ref == nil { |
| continue |
| } |
| ref.decRef() |
| |
| // This NIC matches the interface address. |
| err := n.AddAddressWithOptions(protocol, multicastAddr, NeverPrimaryEndpoint) |
| if err != nil { |
| return err |
| } |
| epFound = true |
| } |
| if !epFound { |
| return tcpip.ErrNoRoute |
| } |
| |
| return nil |
| } |
| |
| // LeaveGroup leaves the given multicast group on every interface that |
| // matches the given interface address. |
| func (s *Stack) LeaveGroup(protocol tcpip.NetworkProtocolNumber, interfaceAddr tcpip.Address, multicastAddr tcpip.Address) *tcpip.Error { |
| s.mu.RLock() |
| defer s.mu.RUnlock() |
| |
| epFound := false |
| for _, n := range s.nics { |
| ref := n.findEndpoint(interfaceAddr) |
| if ref == nil { |
| continue |
| } |
| ref.decRef() |
| |
| // This NIC matches the interface address. |
| err := n.RemoveAddress(multicastAddr) |
| if err != nil { |
| return err |
| } |
| |
| epFound = true |
| } |
| if !epFound { |
| return tcpip.ErrNoRoute |
| } |
| return nil |
| } |
| |
| // NetworkProtocolInstance returns the protocol instance in the stack for the |
| // specified network protocol. This method is public for protocol implementers |
| // and tests to use. |
| func (s *Stack) NetworkProtocolInstance(num tcpip.NetworkProtocolNumber) NetworkProtocol { |
| if p, ok := s.networkProtocols[num]; ok { |
| return p |
| } |
| return nil |
| } |
| |
| // TransportProtocolInstance returns the protocol instance in the stack for the |
| // specified transport protocol. This method is public for protocol implementers |
| // and tests to use. |
| func (s *Stack) TransportProtocolInstance(num tcpip.TransportProtocolNumber) TransportProtocol { |
| if pState, ok := s.transportProtocols[num]; ok { |
| return pState.proto |
| } |
| return nil |
| } |