summaryrefslogtreecommitdiffhomepage
path: root/pkg/tcpip/network/ipv4
diff options
context:
space:
mode:
authorIan Gudger <igudger@google.com>2018-05-01 22:50:55 -0700
committerShentubot <shentubot@google.com>2018-05-01 22:51:41 -0700
commiteb5414ee29f20b1805345820e6174afff84276c2 (patch)
treeec1573b6c72dd90e2d97af6f6ea04cc1f8654311 /pkg/tcpip/network/ipv4
parent65df95516898f077cda44ace15e45e4c777fdaf3 (diff)
Add support for ping sockets
PiperOrigin-RevId: 195049322 Change-Id: I09f6dd58cf10a2e50e53d17d2823d540102913c5
Diffstat (limited to 'pkg/tcpip/network/ipv4')
-rw-r--r--pkg/tcpip/network/ipv4/BUILD17
-rw-r--r--pkg/tcpip/network/ipv4/icmp.go190
-rw-r--r--pkg/tcpip/network/ipv4/icmp_test.go124
3 files changed, 12 insertions, 319 deletions
diff --git a/pkg/tcpip/network/ipv4/BUILD b/pkg/tcpip/network/ipv4/BUILD
index 9df113df1..02d55355c 100644
--- a/pkg/tcpip/network/ipv4/BUILD
+++ b/pkg/tcpip/network/ipv4/BUILD
@@ -1,6 +1,6 @@
package(licenses = ["notice"]) # BSD
-load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")
+load("@io_bazel_rules_go//go:def.bzl", "go_library")
go_library(
name = "ipv4",
@@ -19,20 +19,5 @@ go_library(
"//pkg/tcpip/network/fragmentation",
"//pkg/tcpip/network/hash",
"//pkg/tcpip/stack",
- "//pkg/waiter",
- ],
-)
-
-go_test(
- name = "ipv4_test",
- size = "small",
- srcs = ["icmp_test.go"],
- deps = [
- ":ipv4",
- "//pkg/tcpip",
- "//pkg/tcpip/buffer",
- "//pkg/tcpip/link/channel",
- "//pkg/tcpip/link/sniffer",
- "//pkg/tcpip/stack",
],
)
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) {
-}
diff --git a/pkg/tcpip/network/ipv4/icmp_test.go b/pkg/tcpip/network/ipv4/icmp_test.go
deleted file mode 100644
index c55aa1835..000000000
--- a/pkg/tcpip/network/ipv4/icmp_test.go
+++ /dev/null
@@ -1,124 +0,0 @@
-// 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 ipv4_test
-
-import (
- "context"
- "testing"
- "time"
-
- "gvisor.googlesource.com/gvisor/pkg/tcpip"
- "gvisor.googlesource.com/gvisor/pkg/tcpip/buffer"
- "gvisor.googlesource.com/gvisor/pkg/tcpip/link/channel"
- "gvisor.googlesource.com/gvisor/pkg/tcpip/link/sniffer"
- "gvisor.googlesource.com/gvisor/pkg/tcpip/network/ipv4"
- "gvisor.googlesource.com/gvisor/pkg/tcpip/stack"
-)
-
-const stackAddr = "\x0a\x00\x00\x01"
-
-type testContext struct {
- t *testing.T
- linkEP *channel.Endpoint
- s *stack.Stack
-}
-
-func newTestContext(t *testing.T) *testContext {
- s := stack.New(&tcpip.StdClock{}, []string{ipv4.ProtocolName}, []string{ipv4.PingProtocolName})
-
- const defaultMTU = 65536
- id, linkEP := channel.New(256, defaultMTU, "")
- if testing.Verbose() {
- id = sniffer.New(id)
- }
- if err := s.CreateNIC(1, id); err != nil {
- t.Fatalf("CreateNIC failed: %v", err)
- }
-
- if err := s.AddAddress(1, ipv4.ProtocolNumber, stackAddr); err != nil {
- t.Fatalf("AddAddress failed: %v", err)
- }
-
- s.SetRouteTable([]tcpip.Route{{
- Destination: "\x00\x00\x00\x00",
- Mask: "\x00\x00\x00\x00",
- Gateway: "",
- NIC: 1,
- }})
-
- return &testContext{
- t: t,
- s: s,
- linkEP: linkEP,
- }
-}
-
-func (c *testContext) cleanup() {
- close(c.linkEP.C)
-}
-
-func (c *testContext) loopback() {
- go func() {
- for pkt := range c.linkEP.C {
- v := make(buffer.View, len(pkt.Header)+len(pkt.Payload))
- copy(v, pkt.Header)
- copy(v[len(pkt.Header):], pkt.Payload)
- vv := v.ToVectorisedView([1]buffer.View{})
- c.linkEP.Inject(pkt.Proto, &vv)
- }
- }()
-}
-
-func TestEcho(t *testing.T) {
- c := newTestContext(t)
- defer c.cleanup()
- c.loopback()
-
- ch := make(chan ipv4.PingReply, 1)
- p := ipv4.Pinger{
- Stack: c.s,
- NICID: 1,
- Addr: stackAddr,
- Wait: 10 * time.Millisecond,
- Count: 1, // one ping only
- }
- if err := p.Ping(context.Background(), ch); err != nil {
- t.Fatalf("icmp.Ping failed: %v", err)
- }
-
- ping := <-ch
- if ping.Error != nil {
- t.Errorf("bad ping response: %v", ping.Error)
- }
-}
-
-func TestEchoSequence(t *testing.T) {
- c := newTestContext(t)
- defer c.cleanup()
- c.loopback()
-
- const numPings = 3
- ch := make(chan ipv4.PingReply, numPings)
- p := ipv4.Pinger{
- Stack: c.s,
- NICID: 1,
- Addr: stackAddr,
- Wait: 10 * time.Millisecond,
- Count: numPings,
- }
- if err := p.Ping(context.Background(), ch); err != nil {
- t.Fatalf("icmp.Ping failed: %v", err)
- }
-
- for i := uint16(0); i < numPings; i++ {
- ping := <-ch
- if ping.Error != nil {
- t.Errorf("i=%d bad ping response: %v", i, ping.Error)
- }
- if ping.SeqNumber != i {
- t.Errorf("SeqNumber=%d, want %d", ping.SeqNumber, i)
- }
- }
-}