| // Copyright 2018 The gVisor Authors. |
| // |
| // 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 ( |
| "gvisor.dev/gvisor/pkg/sleep" |
| "gvisor.dev/gvisor/pkg/tcpip" |
| "gvisor.dev/gvisor/pkg/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 |
| } else if remoteAddr == header.IPv4Broadcast { |
| 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, totalLen uint16) uint16 { |
| return header.PseudoHeaderChecksum(protocol, r.LocalAddress, r.RemoteAddress, totalLen) |
| } |
| |
| // Capabilities returns the link-layer capabilities of the route. |
| func (r *Route) Capabilities() LinkEndpointCapabilities { |
| return r.ref.ep.Capabilities() |
| } |
| |
| // GSOMaxSize returns the maximum GSO packet size. |
| func (r *Route) GSOMaxSize() uint32 { |
| if gso, ok := r.ref.ep.(GSOEndpoint); ok { |
| return gso.GSOMaxSize() |
| } |
| return 0 |
| } |
| |
| // 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.isValidForOutgoing() && r.ref.linkCache != nil && r.RemoteLinkAddress == "" |
| } |
| |
| // WritePacket writes the packet through the given route. |
| func (r *Route) WritePacket(gso *GSO, params NetworkHeaderParams, pkt PacketBuffer) *tcpip.Error { |
| if !r.ref.isValidForOutgoing() { |
| return tcpip.ErrInvalidEndpointState |
| } |
| |
| err := r.ref.ep.WritePacket(r, gso, params, pkt) |
| if err != nil { |
| r.Stats().IP.OutgoingPacketErrors.Increment() |
| } else { |
| r.ref.nic.stats.Tx.Packets.Increment() |
| r.ref.nic.stats.Tx.Bytes.IncrementBy(uint64(pkt.Header.UsedLength() + pkt.Data.Size())) |
| } |
| return err |
| } |
| |
| // WritePackets writes a list of n packets through the given route and returns |
| // the number of packets written. |
| func (r *Route) WritePackets(gso *GSO, pkts PacketBufferList, params NetworkHeaderParams) (int, *tcpip.Error) { |
| if !r.ref.isValidForOutgoing() { |
| return 0, tcpip.ErrInvalidEndpointState |
| } |
| |
| n, err := r.ref.ep.WritePackets(r, gso, pkts, params) |
| if err != nil { |
| r.Stats().IP.OutgoingPacketErrors.IncrementBy(uint64(pkts.Len() - n)) |
| } |
| r.ref.nic.stats.Tx.Packets.IncrementBy(uint64(n)) |
| |
| writtenBytes := 0 |
| for i, pb := 0, pkts.Front(); i < n && pb != nil; i, pb = i+1, pb.Next() { |
| writtenBytes += pb.Header.UsedLength() |
| writtenBytes += pb.Data.Size() |
| } |
| |
| r.ref.nic.stats.Tx.Bytes.IncrementBy(uint64(writtenBytes)) |
| return n, err |
| } |
| |
| // WriteHeaderIncludedPacket writes a packet already containing a network |
| // header through the given route. |
| func (r *Route) WriteHeaderIncludedPacket(pkt PacketBuffer) *tcpip.Error { |
| if !r.ref.isValidForOutgoing() { |
| return tcpip.ErrInvalidEndpointState |
| } |
| |
| if err := r.ref.ep.WriteHeaderIncludedPacket(r, pkt); err != nil { |
| r.Stats().IP.OutgoingPacketErrors.Increment() |
| return err |
| } |
| r.ref.nic.stats.Tx.Packets.Increment() |
| r.ref.nic.stats.Tx.Bytes.IncrementBy(uint64(pkt.Data.Size())) |
| return nil |
| } |
| |
| // 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 { |
| if r.ref != nil { |
| r.ref.incRef() |
| } |
| return *r |
| } |
| |
| // MakeLoopedRoute duplicates the given route with special handling for routes |
| // used for sending multicast or broadcast packets. In those cases the |
| // multicast/broadcast address is the remote address when sending out, but for |
| // incoming (looped) packets it becomes the local address. Similarly, the local |
| // interface address that was the local address going out becomes the remote |
| // address coming in. This is different to unicast routes where local and |
| // remote addresses remain the same as they identify location (local vs remote) |
| // not direction (source vs destination). |
| func (r *Route) MakeLoopedRoute() Route { |
| l := r.Clone() |
| if r.RemoteAddress == header.IPv4Broadcast || header.IsV4MulticastAddress(r.RemoteAddress) || header.IsV6MulticastAddress(r.RemoteAddress) { |
| l.RemoteAddress, l.LocalAddress = l.LocalAddress, l.RemoteAddress |
| l.RemoteLinkAddress = l.LocalLinkAddress |
| } |
| return l |
| } |
| |
| // Stack returns the instance of the Stack that owns this route. |
| func (r *Route) Stack() *Stack { |
| return r.ref.stack() |
| } |