| // Copyright 2018 Google LLC |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| package stack |
| |
| import ( |
| "github.com/google/netstack/sleep" |
| "github.com/google/netstack/tcpip" |
| "github.com/google/netstack/tcpip/buffer" |
| "github.com/google/netstack/tcpip/header" |
| ) |
| |
| // Route represents a route through the networking stack to a given destination. |
| type Route struct { |
| // RemoteAddress is the final destination of the route. |
| RemoteAddress tcpip.Address |
| |
| // RemoteLinkAddress is the link-layer (MAC) address of the |
| // final destination of the route. |
| RemoteLinkAddress tcpip.LinkAddress |
| |
| // LocalAddress is the local address where the route starts. |
| LocalAddress tcpip.Address |
| |
| // LocalLinkAddress is the link-layer (MAC) address of the |
| // where the route starts. |
| LocalLinkAddress tcpip.LinkAddress |
| |
| // NextHop is the next node in the path to the destination. |
| NextHop tcpip.Address |
| |
| // NetProto is the network-layer protocol. |
| NetProto tcpip.NetworkProtocolNumber |
| |
| // ref a reference to the network endpoint through which the route |
| // starts. |
| ref *referencedNetworkEndpoint |
| |
| // loop controls where WritePacket should send packets. |
| loop PacketLooping |
| } |
| |
| // makeRoute initializes a new route. It takes ownership of the provided |
| // reference to a network endpoint. |
| func makeRoute(netProto tcpip.NetworkProtocolNumber, localAddr, remoteAddr tcpip.Address, localLinkAddr tcpip.LinkAddress, ref *referencedNetworkEndpoint, handleLocal, multicastLoop bool) Route { |
| loop := PacketOut |
| if handleLocal && localAddr != "" && remoteAddr == localAddr { |
| loop = PacketLoop |
| } else if multicastLoop && (header.IsV4MulticastAddress(remoteAddr) || header.IsV6MulticastAddress(remoteAddr)) { |
| loop |= PacketLoop |
| } |
| |
| return Route{ |
| NetProto: netProto, |
| LocalAddress: localAddr, |
| LocalLinkAddress: localLinkAddr, |
| RemoteAddress: remoteAddr, |
| ref: ref, |
| loop: loop, |
| } |
| } |
| |
| // NICID returns the id of the NIC from which this route originates. |
| func (r *Route) NICID() tcpip.NICID { |
| return r.ref.ep.NICID() |
| } |
| |
| // MaxHeaderLength forwards the call to the network endpoint's implementation. |
| func (r *Route) MaxHeaderLength() uint16 { |
| return r.ref.ep.MaxHeaderLength() |
| } |
| |
| // Stats returns a mutable copy of current stats. |
| func (r *Route) Stats() tcpip.Stats { |
| return r.ref.nic.stack.Stats() |
| } |
| |
| // PseudoHeaderChecksum forwards the call to the network endpoint's |
| // implementation. |
| func (r *Route) PseudoHeaderChecksum(protocol tcpip.TransportProtocolNumber) uint16 { |
| return header.PseudoHeaderChecksum(protocol, r.LocalAddress, r.RemoteAddress) |
| } |
| |
| // Capabilities returns the link-layer capabilities of the route. |
| func (r *Route) Capabilities() LinkEndpointCapabilities { |
| return r.ref.ep.Capabilities() |
| } |
| |
| // Resolve attempts to resolve the link address if necessary. Returns ErrWouldBlock in |
| // case address resolution requires blocking, e.g. wait for ARP reply. Waker is |
| // notified when address resolution is complete (success or not). |
| // |
| // If address resolution is required, ErrNoLinkAddress and a notification channel is |
| // returned for the top level caller to block. Channel is closed once address resolution |
| // is complete (success or not). |
| func (r *Route) Resolve(waker *sleep.Waker) (<-chan struct{}, *tcpip.Error) { |
| if !r.IsResolutionRequired() { |
| // Nothing to do if there is no cache (which does the resolution on cache miss) or |
| // link address is already known. |
| return nil, nil |
| } |
| |
| nextAddr := r.NextHop |
| if nextAddr == "" { |
| // Local link address is already known. |
| if r.RemoteAddress == r.LocalAddress { |
| r.RemoteLinkAddress = r.LocalLinkAddress |
| return nil, nil |
| } |
| nextAddr = r.RemoteAddress |
| } |
| linkAddr, ch, err := r.ref.linkCache.GetLinkAddress(r.ref.nic.ID(), nextAddr, r.LocalAddress, r.NetProto, waker) |
| if err != nil { |
| return ch, err |
| } |
| r.RemoteLinkAddress = linkAddr |
| return nil, nil |
| } |
| |
| // RemoveWaker removes a waker that has been added in Resolve(). |
| func (r *Route) RemoveWaker(waker *sleep.Waker) { |
| nextAddr := r.NextHop |
| if nextAddr == "" { |
| nextAddr = r.RemoteAddress |
| } |
| r.ref.linkCache.RemoveWaker(r.ref.nic.ID(), nextAddr, waker) |
| } |
| |
| // IsResolutionRequired returns true if Resolve() must be called to resolve |
| // the link address before the this route can be written to. |
| func (r *Route) IsResolutionRequired() bool { |
| return r.ref.linkCache != nil && r.RemoteLinkAddress == "" |
| } |
| |
| // WritePacket writes the packet through the given route. |
| func (r *Route) WritePacket(hdr buffer.Prependable, payload buffer.VectorisedView, protocol tcpip.TransportProtocolNumber, ttl uint8) *tcpip.Error { |
| err := r.ref.ep.WritePacket(r, hdr, payload, protocol, ttl, r.loop) |
| if err != nil { |
| r.Stats().IP.OutgoingPacketErrors.Increment() |
| } else { |
| r.ref.nic.stats.Tx.Packets.Increment() |
| r.ref.nic.stats.Tx.Bytes.IncrementBy(uint64(hdr.UsedLength() + payload.Size())) |
| } |
| return err |
| } |
| |
| // DefaultTTL returns the default TTL of the underlying network endpoint. |
| func (r *Route) DefaultTTL() uint8 { |
| return r.ref.ep.DefaultTTL() |
| } |
| |
| // MTU returns the MTU of the underlying network endpoint. |
| func (r *Route) MTU() uint32 { |
| return r.ref.ep.MTU() |
| } |
| |
| // Release frees all resources associated with the route. |
| func (r *Route) Release() { |
| if r.ref != nil { |
| r.ref.decRef() |
| r.ref = nil |
| } |
| } |
| |
| // Clone Clone a route such that the original one can be released and the new |
| // one will remain valid. |
| func (r *Route) Clone() Route { |
| r.ref.incRef() |
| return *r |
| } |