mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
Add support for ping sockets
PiperOrigin-RevId: 195049322 Change-Id: I09f6dd58cf10a2e50e53d17d2823d540102913c5
This commit is contained in:
@@ -50,6 +50,7 @@ go_library(
|
||||
"//pkg/syserror",
|
||||
"//pkg/tcpip",
|
||||
"//pkg/tcpip/buffer",
|
||||
"//pkg/tcpip/header",
|
||||
"//pkg/tcpip/network/ipv4",
|
||||
"//pkg/tcpip/network/ipv6",
|
||||
"//pkg/tcpip/stack",
|
||||
|
||||
@@ -23,6 +23,7 @@ import (
|
||||
"gvisor.googlesource.com/gvisor/pkg/sentry/socket"
|
||||
"gvisor.googlesource.com/gvisor/pkg/syserr"
|
||||
"gvisor.googlesource.com/gvisor/pkg/tcpip"
|
||||
"gvisor.googlesource.com/gvisor/pkg/tcpip/header"
|
||||
"gvisor.googlesource.com/gvisor/pkg/tcpip/network/ipv4"
|
||||
"gvisor.googlesource.com/gvisor/pkg/tcpip/network/ipv6"
|
||||
"gvisor.googlesource.com/gvisor/pkg/tcpip/transport/tcp"
|
||||
@@ -37,8 +38,8 @@ type provider struct {
|
||||
netProto tcpip.NetworkProtocolNumber
|
||||
}
|
||||
|
||||
// GetTransportProtocol figures out transport protocol. Currently only TCP and
|
||||
// UDP are supported.
|
||||
// GetTransportProtocol figures out transport protocol. Currently only TCP,
|
||||
// UDP, and ICMP are supported.
|
||||
func GetTransportProtocol(stype unix.SockType, protocol int) (tcpip.TransportProtocolNumber, *syserr.Error) {
|
||||
switch stype {
|
||||
case linux.SOCK_STREAM:
|
||||
@@ -48,14 +49,16 @@ func GetTransportProtocol(stype unix.SockType, protocol int) (tcpip.TransportPro
|
||||
return tcp.ProtocolNumber, nil
|
||||
|
||||
case linux.SOCK_DGRAM:
|
||||
if protocol != 0 && protocol != syscall.IPPROTO_UDP {
|
||||
return 0, syserr.ErrInvalidArgument
|
||||
switch protocol {
|
||||
case 0, syscall.IPPROTO_UDP:
|
||||
return udp.ProtocolNumber, nil
|
||||
case syscall.IPPROTO_ICMP:
|
||||
return header.ICMPv4ProtocolNumber, nil
|
||||
case syscall.IPPROTO_ICMPV6:
|
||||
return header.ICMPv6ProtocolNumber, nil
|
||||
}
|
||||
return udp.ProtocolNumber, nil
|
||||
|
||||
default:
|
||||
return 0, syserr.ErrInvalidArgument
|
||||
}
|
||||
return 0, syserr.ErrInvalidArgument
|
||||
}
|
||||
|
||||
// Socket creates a new socket object for the AF_INET or AF_INET6 family.
|
||||
|
||||
@@ -791,7 +791,7 @@ func recvFrom(t *kernel.Task, fd kdefs.FD, bufPtr usermem.Addr, bufLen uint64, f
|
||||
}
|
||||
|
||||
// Reject flags that we don't handle yet.
|
||||
if flags & ^(linux.MSG_DONTWAIT|linux.MSG_NOSIGNAL|linux.MSG_PEEK|linux.MSG_TRUNC) != 0 {
|
||||
if flags & ^(linux.MSG_DONTWAIT|linux.MSG_NOSIGNAL|linux.MSG_PEEK|linux.MSG_TRUNC|linux.MSG_CONFIRM) != 0 {
|
||||
return 0, syscall.EINVAL
|
||||
}
|
||||
|
||||
|
||||
@@ -30,5 +30,6 @@ go_test(
|
||||
"//pkg/tcpip/link/sniffer",
|
||||
"//pkg/tcpip/network/ipv4",
|
||||
"//pkg/tcpip/stack",
|
||||
"//pkg/tcpip/transport/ping",
|
||||
],
|
||||
)
|
||||
|
||||
@@ -16,6 +16,7 @@ import (
|
||||
"gvisor.googlesource.com/gvisor/pkg/tcpip/network/arp"
|
||||
"gvisor.googlesource.com/gvisor/pkg/tcpip/network/ipv4"
|
||||
"gvisor.googlesource.com/gvisor/pkg/tcpip/stack"
|
||||
"gvisor.googlesource.com/gvisor/pkg/tcpip/transport/ping"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -32,7 +33,7 @@ type testContext struct {
|
||||
}
|
||||
|
||||
func newTestContext(t *testing.T) *testContext {
|
||||
s := stack.New(&tcpip.StdClock{}, []string{ipv4.ProtocolName, arp.ProtocolName}, []string{ipv4.PingProtocolName})
|
||||
s := stack.New(&tcpip.StdClock{}, []string{ipv4.ProtocolName, arp.ProtocolName}, []string{ping.ProtocolName4})
|
||||
|
||||
const defaultMTU = 65536
|
||||
id, linkEP := channel.New(256, defaultMTU, stackLinkAddr)
|
||||
|
||||
@@ -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",
|
||||
],
|
||||
)
|
||||
|
||||
+11
-179
@@ -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) {
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
package(licenses = ["notice"]) # BSD
|
||||
|
||||
load("@io_bazel_rules_go//go:def.bzl", "go_library")
|
||||
load("//tools/go_generics:defs.bzl", "go_template_instance")
|
||||
load("//tools/go_stateify:defs.bzl", "go_stateify")
|
||||
|
||||
go_stateify(
|
||||
name = "ping_state",
|
||||
srcs = [
|
||||
"endpoint.go",
|
||||
"endpoint_state.go",
|
||||
"ping_packet_list.go",
|
||||
],
|
||||
out = "ping_state.go",
|
||||
imports = ["gvisor.googlesource.com/gvisor/pkg/tcpip/buffer"],
|
||||
package = "ping",
|
||||
)
|
||||
|
||||
go_template_instance(
|
||||
name = "ping_packet_list",
|
||||
out = "ping_packet_list.go",
|
||||
package = "ping",
|
||||
prefix = "pingPacket",
|
||||
template = "//pkg/ilist:generic_list",
|
||||
types = {
|
||||
"Linker": "*pingPacket",
|
||||
},
|
||||
)
|
||||
|
||||
go_library(
|
||||
name = "ping",
|
||||
srcs = [
|
||||
"endpoint.go",
|
||||
"endpoint_state.go",
|
||||
"ping_packet_list.go",
|
||||
"ping_state.go",
|
||||
"protocol.go",
|
||||
],
|
||||
importpath = "gvisor.googlesource.com/gvisor/pkg/tcpip/transport/ping",
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
"//pkg/sleep",
|
||||
"//pkg/state",
|
||||
"//pkg/tcpip",
|
||||
"//pkg/tcpip/buffer",
|
||||
"//pkg/tcpip/header",
|
||||
"//pkg/tcpip/stack",
|
||||
"//pkg/waiter",
|
||||
],
|
||||
)
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,61 @@
|
||||
// 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 ping
|
||||
|
||||
import (
|
||||
"gvisor.googlesource.com/gvisor/pkg/tcpip"
|
||||
"gvisor.googlesource.com/gvisor/pkg/tcpip/buffer"
|
||||
"gvisor.googlesource.com/gvisor/pkg/tcpip/stack"
|
||||
)
|
||||
|
||||
// saveData saves pingPacket.data field.
|
||||
func (p *pingPacket) saveData() buffer.VectorisedView {
|
||||
// We cannot save p.data directly as p.data.views may alias to p.views,
|
||||
// which is not allowed by state framework (in-struct pointer).
|
||||
return p.data.Clone(nil)
|
||||
}
|
||||
|
||||
// loadData loads pingPacket.data field.
|
||||
func (p *pingPacket) loadData(data buffer.VectorisedView) {
|
||||
// NOTE: We cannot do the p.data = data.Clone(p.views[:]) optimization
|
||||
// here because data.views is not guaranteed to be loaded by now. Plus,
|
||||
// data.views will be allocated anyway so there really is little point
|
||||
// of utilizing p.views for data.views.
|
||||
p.data = data
|
||||
}
|
||||
|
||||
// beforeSave is invoked by stateify.
|
||||
func (e *endpoint) beforeSave() {
|
||||
// Stop incoming packets from being handled (and mutate endpoint state).
|
||||
e.rcvMu.Lock()
|
||||
}
|
||||
|
||||
// afterLoad is invoked by stateify.
|
||||
func (e *endpoint) afterLoad() {
|
||||
e.stack = stack.StackFromEnv
|
||||
|
||||
if e.state != stateBound && e.state != stateConnected {
|
||||
return
|
||||
}
|
||||
|
||||
var err *tcpip.Error
|
||||
if e.state == stateConnected {
|
||||
e.route, err = e.stack.FindRoute(e.regNICID, e.bindAddr, e.id.RemoteAddress, e.netProto)
|
||||
if err != nil {
|
||||
panic(*err)
|
||||
}
|
||||
|
||||
e.id.LocalAddress = e.route.LocalAddress
|
||||
} else if len(e.id.LocalAddress) != 0 { // stateBound
|
||||
if e.stack.CheckLocalAddress(e.regNICID, e.netProto, e.id.LocalAddress) == 0 {
|
||||
panic(tcpip.ErrBadLocalAddress)
|
||||
}
|
||||
}
|
||||
|
||||
e.id, err = e.registerWithStack(e.regNICID, []tcpip.NetworkProtocolNumber{e.netProto}, e.id)
|
||||
if err != nil {
|
||||
panic(*err)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
// 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 ping contains the implementation of the ICMP and IPv6-ICMP transport
|
||||
// protocols for use in ping. To use it in the networking stack, this package
|
||||
// must be added to the project, and
|
||||
// activated on the stack by passing ping.ProtocolName (or "ping") and/or
|
||||
// ping.ProtocolName6 (or "ping6") as one of the transport protocols when
|
||||
// calling stack.New(). Then endpoints can be created by passing
|
||||
// ping.ProtocolNumber or ping.ProtocolNumber6 as the transport protocol number
|
||||
// when calling Stack.NewEndpoint().
|
||||
package ping
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
|
||||
"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"
|
||||
)
|
||||
|
||||
const (
|
||||
// ProtocolName4 is the string representation of the ping protocol name.
|
||||
ProtocolName4 = "ping4"
|
||||
|
||||
// ProtocolNumber4 is the ICMP protocol number.
|
||||
ProtocolNumber4 = header.ICMPv4ProtocolNumber
|
||||
|
||||
// ProtocolName6 is the string representation of the ping protocol name.
|
||||
ProtocolName6 = "ping6"
|
||||
|
||||
// ProtocolNumber6 is the IPv6-ICMP protocol number.
|
||||
ProtocolNumber6 = header.ICMPv6ProtocolNumber
|
||||
)
|
||||
|
||||
type protocol struct {
|
||||
number tcpip.TransportProtocolNumber
|
||||
}
|
||||
|
||||
// Number returns the ICMP protocol number.
|
||||
func (p *protocol) Number() tcpip.TransportProtocolNumber {
|
||||
return p.number
|
||||
}
|
||||
|
||||
func (p *protocol) netProto() tcpip.NetworkProtocolNumber {
|
||||
switch p.number {
|
||||
case ProtocolNumber4:
|
||||
return header.IPv4ProtocolNumber
|
||||
case ProtocolNumber6:
|
||||
return header.IPv6ProtocolNumber
|
||||
}
|
||||
panic(fmt.Sprint("unknown protocol number: ", p.number))
|
||||
}
|
||||
|
||||
// NewEndpoint creates a new ping endpoint.
|
||||
func (p *protocol) NewEndpoint(stack *stack.Stack, netProto tcpip.NetworkProtocolNumber, waiterQueue *waiter.Queue) (tcpip.Endpoint, *tcpip.Error) {
|
||||
if netProto != p.netProto() {
|
||||
return nil, tcpip.ErrUnknownProtocol
|
||||
}
|
||||
return newEndpoint(stack, netProto, waiterQueue), nil
|
||||
}
|
||||
|
||||
// MinimumPacketSize returns the minimum valid ping packet size.
|
||||
func (p *protocol) MinimumPacketSize() int {
|
||||
switch p.number {
|
||||
case ProtocolNumber4:
|
||||
return header.ICMPv4EchoMinimumSize
|
||||
case ProtocolNumber6:
|
||||
return header.ICMPv6EchoMinimumSize
|
||||
}
|
||||
panic(fmt.Sprint("unknown protocol number: ", p.number))
|
||||
}
|
||||
|
||||
// ParsePorts returns the source and destination ports stored in the given udp
|
||||
// packet.
|
||||
func (*protocol) ParsePorts(v buffer.View) (src, dst uint16, err *tcpip.Error) {
|
||||
return 0, binary.BigEndian.Uint16(v[header.ICMPv4MinimumSize:]), nil
|
||||
}
|
||||
|
||||
// HandleUnknownDestinationPacket handles packets targeted at this protocol but
|
||||
// that don't match any existing endpoint.
|
||||
func (p *protocol) HandleUnknownDestinationPacket(*stack.Route, stack.TransportEndpointID, *buffer.VectorisedView) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// SetOption implements TransportProtocol.SetOption.
|
||||
func (p *protocol) SetOption(option interface{}) *tcpip.Error {
|
||||
return tcpip.ErrUnknownProtocolOption
|
||||
}
|
||||
|
||||
// Option implements TransportProtocol.Option.
|
||||
func (p *protocol) Option(option interface{}) *tcpip.Error {
|
||||
return tcpip.ErrUnknownProtocolOption
|
||||
}
|
||||
|
||||
func init() {
|
||||
stack.RegisterTransportProtocolFactory(ProtocolName4, func() stack.TransportProtocol {
|
||||
return &protocol{ProtocolNumber4}
|
||||
})
|
||||
|
||||
// TODO: Support IPv6.
|
||||
}
|
||||
@@ -13,7 +13,7 @@ import (
|
||||
|
||||
// saveData saves udpPacket.data field.
|
||||
func (u *udpPacket) saveData() buffer.VectorisedView {
|
||||
// We canoot save u.data directly as u.data.views may alias to u.views,
|
||||
// We cannot save u.data directly as u.data.views may alias to u.views,
|
||||
// which is not allowed by state framework (in-struct pointer).
|
||||
return u.data.Clone(nil)
|
||||
}
|
||||
|
||||
@@ -64,6 +64,7 @@ go_library(
|
||||
"//pkg/tcpip/network/ipv4",
|
||||
"//pkg/tcpip/network/ipv6",
|
||||
"//pkg/tcpip/stack",
|
||||
"//pkg/tcpip/transport/ping",
|
||||
"//pkg/tcpip/transport/tcp",
|
||||
"//pkg/tcpip/transport/udp",
|
||||
"//pkg/urpc",
|
||||
|
||||
@@ -43,6 +43,7 @@ import (
|
||||
"gvisor.googlesource.com/gvisor/pkg/tcpip/network/ipv4"
|
||||
"gvisor.googlesource.com/gvisor/pkg/tcpip/network/ipv6"
|
||||
"gvisor.googlesource.com/gvisor/pkg/tcpip/stack"
|
||||
"gvisor.googlesource.com/gvisor/pkg/tcpip/transport/ping"
|
||||
"gvisor.googlesource.com/gvisor/pkg/tcpip/transport/tcp"
|
||||
"gvisor.googlesource.com/gvisor/pkg/tcpip/transport/udp"
|
||||
"gvisor.googlesource.com/gvisor/runsc/boot/filter"
|
||||
@@ -346,7 +347,7 @@ func newEmptyNetworkStack(conf *Config, clock tcpip.Clock) inet.Stack {
|
||||
case NetworkNone, NetworkSandbox:
|
||||
// NetworkNone sets up loopback using netstack.
|
||||
netProtos := []string{ipv4.ProtocolName, ipv6.ProtocolName, arp.ProtocolName}
|
||||
protoNames := []string{tcp.ProtocolName, udp.ProtocolName}
|
||||
protoNames := []string{tcp.ProtocolName, udp.ProtocolName, ping.ProtocolName4}
|
||||
return &epsocket.Stack{stack.New(clock, netProtos, protoNames)}
|
||||
|
||||
default:
|
||||
|
||||
Reference in New Issue
Block a user