diff options
Diffstat (limited to 'pkg/tcpip/network/ipv4/icmp.go')
-rw-r--r-- | pkg/tcpip/network/ipv4/icmp.go | 190 |
1 files changed, 11 insertions, 179 deletions
diff --git a/pkg/tcpip/network/ipv4/icmp.go b/pkg/tcpip/network/ipv4/icmp.go index ffd761350..3c382fdc2 100644 --- a/pkg/tcpip/network/ipv4/icmp.go +++ b/pkg/tcpip/network/ipv4/icmp.go @@ -5,26 +5,14 @@ package ipv4 import ( - "context" "encoding/binary" - "time" "gvisor.googlesource.com/gvisor/pkg/tcpip" "gvisor.googlesource.com/gvisor/pkg/tcpip/buffer" "gvisor.googlesource.com/gvisor/pkg/tcpip/header" "gvisor.googlesource.com/gvisor/pkg/tcpip/stack" - "gvisor.googlesource.com/gvisor/pkg/waiter" ) -// PingProtocolName is a pseudo transport protocol used to handle ping replies. -// Use it when constructing a stack that intends to use ipv4.Ping. -const PingProtocolName = "icmpv4ping" - -// pingProtocolNumber is a fake transport protocol used to -// deliver incoming ICMP echo replies. The ICMP identifier -// number is used as a port number for multiplexing. -const pingProtocolNumber tcpip.TransportProtocolNumber = 256 + 11 - // handleControl handles the case when an ICMP packet contains the headers of // the original packet that caused the ICMP one to be sent. This information is // used to find out which transport endpoint must be notified about the ICMP @@ -78,7 +66,10 @@ func (e *endpoint) handleICMP(r *stack.Route, vv *buffer.VectorisedView) { } case header.ICMPv4EchoReply: - e.dispatcher.DeliverTransportPacket(r, pingProtocolNumber, vv) + if len(v) < header.ICMPv4EchoMinimumSize { + return + } + e.dispatcher.DeliverTransportPacket(r, header.ICMPv4ProtocolNumber, vv) case header.ICMPv4DstUnreachable: if len(v) < header.ICMPv4DstUnreachableMinimumSize { @@ -104,179 +95,20 @@ type echoRequest struct { func (e *endpoint) echoReplier() { for req := range e.echoRequests { - sendICMPv4(&req.r, header.ICMPv4EchoReply, 0, req.v) + sendPing4(&req.r, 0, req.v) req.r.Release() } } -func sendICMPv4(r *stack.Route, typ header.ICMPv4Type, code byte, data buffer.View) *tcpip.Error { - hdr := buffer.NewPrependable(header.ICMPv4MinimumSize + int(r.MaxHeaderLength())) +func sendPing4(r *stack.Route, code byte, data buffer.View) *tcpip.Error { + hdr := buffer.NewPrependable(header.ICMPv4EchoMinimumSize + int(r.MaxHeaderLength())) - icmpv4 := header.ICMPv4(hdr.Prepend(header.ICMPv4MinimumSize)) - icmpv4.SetType(typ) + icmpv4 := header.ICMPv4(hdr.Prepend(header.ICMPv4EchoMinimumSize)) + icmpv4.SetType(header.ICMPv4EchoReply) icmpv4.SetCode(code) + copy(icmpv4[header.ICMPv4MinimumSize:], data) + data = data[header.ICMPv4EchoMinimumSize-header.ICMPv4MinimumSize:] icmpv4.SetChecksum(^header.Checksum(icmpv4, header.Checksum(data, 0))) return r.WritePacket(&hdr, data, header.ICMPv4ProtocolNumber) } - -// A Pinger can send echo requests to an address. -type Pinger struct { - Stack *stack.Stack - NICID tcpip.NICID - Addr tcpip.Address - LocalAddr tcpip.Address // optional - Wait time.Duration // if zero, defaults to 1 second - Count uint16 // if zero, defaults to MaxUint16 -} - -// Ping sends echo requests to an ICMPv4 endpoint. -// Responses are streamed to the channel ch. -func (p *Pinger) Ping(ctx context.Context, ch chan<- PingReply) *tcpip.Error { - count := p.Count - if count == 0 { - count = 1<<16 - 1 - } - wait := p.Wait - if wait == 0 { - wait = 1 * time.Second - } - - r, err := p.Stack.FindRoute(p.NICID, p.LocalAddr, p.Addr, ProtocolNumber) - if err != nil { - return err - } - - netProtos := []tcpip.NetworkProtocolNumber{ProtocolNumber} - ep := &pingEndpoint{ - stack: p.Stack, - pktCh: make(chan buffer.View, 1), - } - id := stack.TransportEndpointID{ - LocalAddress: r.LocalAddress, - RemoteAddress: p.Addr, - } - - _, err = p.Stack.PickEphemeralPort(func(port uint16) (bool, *tcpip.Error) { - id.LocalPort = port - err := p.Stack.RegisterTransportEndpoint(p.NICID, netProtos, pingProtocolNumber, id, ep) - switch err { - case nil: - return true, nil - case tcpip.ErrPortInUse: - return false, nil - default: - return false, err - } - }) - if err != nil { - return err - } - defer p.Stack.UnregisterTransportEndpoint(p.NICID, netProtos, pingProtocolNumber, id) - - v := buffer.NewView(4) - binary.BigEndian.PutUint16(v[0:], id.LocalPort) - - start := time.Now() - - done := make(chan struct{}) - go func(count int) { - loop: - for ; count > 0; count-- { - select { - case v := <-ep.pktCh: - seq := binary.BigEndian.Uint16(v[header.ICMPv4MinimumSize+2:]) - ch <- PingReply{ - Duration: time.Since(start) - time.Duration(seq)*wait, - SeqNumber: seq, - } - case <-ctx.Done(): - break loop - } - } - close(done) - }(int(count)) - defer func() { <-done }() - - t := time.NewTicker(wait) - defer t.Stop() - for seq := uint16(0); seq < count; seq++ { - select { - case <-t.C: - case <-ctx.Done(): - return nil - } - binary.BigEndian.PutUint16(v[2:], seq) - sent := time.Now() - if err := sendICMPv4(&r, header.ICMPv4Echo, 0, v); err != nil { - ch <- PingReply{ - Error: err, - Duration: time.Since(sent), - SeqNumber: seq, - } - } - } - return nil -} - -// PingReply summarizes an ICMP echo reply. -type PingReply struct { - Error *tcpip.Error // reports any errors sending a ping request - Duration time.Duration - SeqNumber uint16 -} - -type pingProtocol struct{} - -func (*pingProtocol) NewEndpoint(stack *stack.Stack, netProto tcpip.NetworkProtocolNumber, waiterQueue *waiter.Queue) (tcpip.Endpoint, *tcpip.Error) { - return nil, tcpip.ErrNotSupported // endpoints are created directly -} - -func (*pingProtocol) Number() tcpip.TransportProtocolNumber { return pingProtocolNumber } - -func (*pingProtocol) MinimumPacketSize() int { return header.ICMPv4EchoMinimumSize } - -func (*pingProtocol) ParsePorts(v buffer.View) (src, dst uint16, err *tcpip.Error) { - ident := binary.BigEndian.Uint16(v[4:]) - return 0, ident, nil -} - -func (*pingProtocol) HandleUnknownDestinationPacket(*stack.Route, stack.TransportEndpointID, *buffer.VectorisedView) bool { - return true -} - -// SetOption implements TransportProtocol.SetOption. -func (p *pingProtocol) SetOption(option interface{}) *tcpip.Error { - return tcpip.ErrUnknownProtocolOption -} - -// Option implements TransportProtocol.Option. -func (p *pingProtocol) Option(option interface{}) *tcpip.Error { - return tcpip.ErrUnknownProtocolOption -} - -func init() { - stack.RegisterTransportProtocolFactory(PingProtocolName, func() stack.TransportProtocol { - return &pingProtocol{} - }) -} - -type pingEndpoint struct { - stack *stack.Stack - pktCh chan buffer.View -} - -func (e *pingEndpoint) Close() { - close(e.pktCh) -} - -func (e *pingEndpoint) HandlePacket(r *stack.Route, id stack.TransportEndpointID, vv *buffer.VectorisedView) { - select { - case e.pktCh <- vv.ToView(): - default: - } -} - -// HandleControlPacket implements stack.TransportEndpoint.HandleControlPacket. -func (e *pingEndpoint) HandleControlPacket(id stack.TransportEndpointID, typ stack.ControlType, extra uint32, vv *buffer.VectorisedView) { -} |