blob: ace5520da810834c48b34c2556b456eb90db79c3 [file] [log] [blame]
// Copyright 2017 The Fuchsia 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 eth
import (
"log"
"syscall/zx"
"syscall/zx/mxerror"
"netstack/trace"
"github.com/google/netstack/tcpip"
"github.com/google/netstack/tcpip/buffer"
"github.com/google/netstack/tcpip/header"
"github.com/google/netstack/tcpip/stack"
)
const debug = false
var _ stack.LinkEndpoint = (*endpoint)(nil)
type endpoint struct {
client *Client
dispatcher stack.NetworkDispatcher
}
func (e *endpoint) MTU() uint32 { return e.client.Info.Mtu }
func (e *endpoint) Capabilities() stack.LinkEndpointCapabilities {
return stack.CapabilityResolutionRequired
}
func (e *endpoint) MaxHeaderLength() uint16 { return header.EthernetMinimumSize }
func (e *endpoint) LinkAddress() tcpip.LinkAddress {
return tcpip.LinkAddress(e.client.Info.Mac.Octets[:])
}
func (e *endpoint) IsAttached() bool {
return e.dispatcher != nil
}
func (e *endpoint) WritePacket(r *stack.Route, hdr buffer.Prependable, payload buffer.VectorisedView, protocol tcpip.NetworkProtocolNumber) *tcpip.Error {
if r.LocalAddress != "" && r.LocalAddress == r.RemoteAddress {
views := make([]buffer.View, 1, 1+len(payload.Views()))
views[0] = hdr.View()
views = append(views, payload.Views()...)
vv := buffer.NewVectorisedView(len(views[0])+payload.Size(), views)
e.dispatcher.DeliverNetworkPacket(e, r.RemoteLinkAddress, r.LocalLinkAddress, protocol, vv)
return nil
}
trace.DebugTrace("eth write")
// Call AllocForSend until it succeeds. After each failure, call
// WaitSend.
buf := e.client.AllocForSend()
for ; buf == nil; buf = e.client.AllocForSend() {
if err := e.client.WaitSend(); err != nil {
trace.DebugDrop("link: alloc error: %v", err)
return tcpip.ErrWouldBlock
}
}
ethHdr := &header.EthernetFields{
Type: protocol,
}
// Preserve the src address if it's set in the route.
if r.LocalLinkAddress != "" {
ethHdr.SrcAddr = r.LocalLinkAddress
} else {
ethHdr.SrcAddr = tcpip.LinkAddress(e.client.Info.Mac.Octets[:])
}
switch {
case header.IsV4MulticastAddress(r.RemoteAddress):
// RFC 1112.6.4
ethHdr.DstAddr = tcpip.LinkAddress([]byte{0x01, 0x00, 0x5e, r.RemoteAddress[1] & 0x7f, r.RemoteAddress[2], r.RemoteAddress[3]})
case header.IsV6MulticastAddress(r.RemoteAddress):
// RFC 2464.7
ethHdr.DstAddr = tcpip.LinkAddress([]byte{0x33, 0x33, r.RemoteAddress[12], r.RemoteAddress[13], r.RemoteAddress[14], r.RemoteAddress[15]})
default:
ethHdr.DstAddr = r.RemoteLinkAddress
}
header.Ethernet(buf).Encode(ethHdr)
used := header.EthernetMinimumSize
used += copy(buf[used:], hdr.View())
for _, v := range payload.Views() {
used += copy(buf[used:], v)
}
if err := e.client.Send(buf[:used]); err != nil {
trace.DebugDrop("link: send error: %v", err)
return tcpip.ErrWouldBlock
}
return nil
}
func (e *endpoint) Attach(dispatcher stack.NetworkDispatcher) {
trace.DebugTraceDeep(5, "eth attach")
go func() {
if err := func() error {
for {
b, err := e.client.Recv()
switch mxerror.Status(err) {
case zx.ErrOk:
v := append(buffer.View(nil), b...)
e.client.Free(b)
eth := header.Ethernet(v)
v.TrimFront(header.EthernetMinimumSize)
dispatcher.DeliverNetworkPacket(e, eth.SourceAddress(), eth.DestinationAddress(), eth.Type(), v.ToVectorisedView())
case zx.ErrShouldWait:
e.client.WaitRecv()
default:
return err
}
}
}(); err != nil {
log.Printf("dispatch error: %v", err)
}
}()
e.dispatcher = dispatcher
}
func NewLinkEndpoint(client *Client) *endpoint {
return &endpoint{client: client}
}