mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
As per https://pkg.go.dev/math/rand#Seed: "If Seed is not called, the generator is seeded randomly at program startup." "Prior to Go 1.20, the generator was seeded like Seed(1) at program startup. To force the old behavior, call Seed(1) at program startup." "As of Go 1.20 there is no reason to call Seed with a random value." rand.Seed() is deprecated. Followup to #11015. PiperOrigin-RevId: 685052229
235 lines
5.6 KiB
Go
235 lines
5.6 KiB
Go
// 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.
|
|
|
|
//go:build linux
|
|
// +build linux
|
|
|
|
// This sample creates a stack with TCP and IPv4 protocols on top of a TUN
|
|
// device, and listens on a port. Data received by the server in the accepted
|
|
// connections is echoed back to the clients.
|
|
package main
|
|
|
|
import (
|
|
"bytes"
|
|
"flag"
|
|
"io"
|
|
"log"
|
|
"net"
|
|
"os"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"gvisor.dev/gvisor/pkg/rawfile"
|
|
"gvisor.dev/gvisor/pkg/tcpip"
|
|
"gvisor.dev/gvisor/pkg/tcpip/link/fdbased"
|
|
"gvisor.dev/gvisor/pkg/tcpip/link/tun"
|
|
"gvisor.dev/gvisor/pkg/tcpip/network/arp"
|
|
"gvisor.dev/gvisor/pkg/tcpip/network/ipv4"
|
|
"gvisor.dev/gvisor/pkg/tcpip/network/ipv6"
|
|
"gvisor.dev/gvisor/pkg/tcpip/stack"
|
|
"gvisor.dev/gvisor/pkg/tcpip/transport/tcp"
|
|
"gvisor.dev/gvisor/pkg/waiter"
|
|
)
|
|
|
|
var tap = flag.Bool("tap", false, "use tap instead of tun")
|
|
var mac = flag.String("mac", "aa:00:01:01:01:01", "mac address to use in tap device")
|
|
|
|
type endpointWriter struct {
|
|
ep tcpip.Endpoint
|
|
}
|
|
|
|
type tcpipError struct {
|
|
inner tcpip.Error
|
|
}
|
|
|
|
func (e *tcpipError) Error() string {
|
|
return e.inner.String()
|
|
}
|
|
|
|
func (e *endpointWriter) Write(p []byte) (int, error) {
|
|
var r bytes.Reader
|
|
r.Reset(p)
|
|
n, err := e.ep.Write(&r, tcpip.WriteOptions{})
|
|
if err != nil {
|
|
return int(n), &tcpipError{
|
|
inner: err,
|
|
}
|
|
}
|
|
if n != int64(len(p)) {
|
|
return int(n), io.ErrShortWrite
|
|
}
|
|
return int(n), nil
|
|
}
|
|
|
|
func echo(wq *waiter.Queue, ep tcpip.Endpoint) {
|
|
defer ep.Close()
|
|
|
|
// Create wait queue entry that notifies a channel.
|
|
waitEntry, notifyCh := waiter.NewChannelEntry(waiter.ReadableEvents)
|
|
wq.EventRegister(&waitEntry)
|
|
defer wq.EventUnregister(&waitEntry)
|
|
|
|
w := endpointWriter{
|
|
ep: ep,
|
|
}
|
|
|
|
for {
|
|
var buf bytes.Buffer
|
|
if _, err := ep.Read(&buf, tcpip.ReadOptions{}); err != nil {
|
|
if _, ok := err.(*tcpip.ErrWouldBlock); ok {
|
|
<-notifyCh
|
|
continue
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
if _, err := w.Write(buf.Bytes()); err != nil {
|
|
return
|
|
}
|
|
}
|
|
}
|
|
|
|
func main() {
|
|
flag.Parse()
|
|
if len(flag.Args()) != 3 {
|
|
log.Fatal("Usage: ", os.Args[0], " <tun-device> <local-address> <local-port>")
|
|
}
|
|
|
|
tunName := flag.Arg(0)
|
|
addrName := flag.Arg(1)
|
|
portName := flag.Arg(2)
|
|
|
|
// Parse the mac address.
|
|
maddr, err := net.ParseMAC(*mac)
|
|
if err != nil {
|
|
log.Fatalf("Bad MAC address: %v", *mac)
|
|
}
|
|
|
|
// Parse the IP address. Support both ipv4 and ipv6.
|
|
parsedAddr := net.ParseIP(addrName)
|
|
if parsedAddr == nil {
|
|
log.Fatalf("Bad IP address: %v", addrName)
|
|
}
|
|
|
|
var addrWithPrefix tcpip.AddressWithPrefix
|
|
var proto tcpip.NetworkProtocolNumber
|
|
if parsedAddr.To4() != nil {
|
|
addrWithPrefix = tcpip.AddrFromSlice(parsedAddr.To4()).WithPrefix()
|
|
proto = ipv4.ProtocolNumber
|
|
} else if parsedAddr.To16() != nil {
|
|
addrWithPrefix = tcpip.AddrFromSlice(parsedAddr.To16()).WithPrefix()
|
|
proto = ipv6.ProtocolNumber
|
|
} else {
|
|
log.Fatalf("Unknown IP type: %v", addrName)
|
|
}
|
|
|
|
localPort, err := strconv.Atoi(portName)
|
|
if err != nil {
|
|
log.Fatalf("Unable to convert port %v: %v", portName, err)
|
|
}
|
|
|
|
// Create the stack with ip and tcp protocols, then add a tun-based
|
|
// NIC and address.
|
|
s := stack.New(stack.Options{
|
|
NetworkProtocols: []stack.NetworkProtocolFactory{ipv4.NewProtocol, ipv6.NewProtocol, arp.NewProtocol},
|
|
TransportProtocols: []stack.TransportProtocolFactory{tcp.NewProtocol},
|
|
})
|
|
|
|
mtu, err := rawfile.GetMTU(tunName)
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
|
|
var fd int
|
|
if *tap {
|
|
fd, err = tun.OpenTAP(tunName)
|
|
} else {
|
|
fd, err = tun.Open(tunName)
|
|
}
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
|
|
linkEP, err := fdbased.New(&fdbased.Options{
|
|
FDs: []int{fd},
|
|
MTU: mtu,
|
|
EthernetHeader: *tap,
|
|
Address: tcpip.LinkAddress(maddr),
|
|
})
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
if err := s.CreateNIC(1, linkEP); err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
|
|
protocolAddr := tcpip.ProtocolAddress{
|
|
Protocol: proto,
|
|
AddressWithPrefix: addrWithPrefix,
|
|
}
|
|
if err := s.AddProtocolAddress(1, protocolAddr, stack.AddressProperties{}); err != nil {
|
|
log.Fatalf("AddProtocolAddress(%d, %+v, {}): %s", 1, protocolAddr, err)
|
|
}
|
|
|
|
subnet, err := tcpip.NewSubnet(tcpip.AddrFromSlice([]byte(strings.Repeat("\x00", addrWithPrefix.Address.Len()))), tcpip.MaskFrom(strings.Repeat("\x00", addrWithPrefix.Address.Len())))
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
|
|
// Add default route.
|
|
s.SetRouteTable([]tcpip.Route{
|
|
{
|
|
Destination: subnet,
|
|
NIC: 1,
|
|
},
|
|
})
|
|
|
|
// Create TCP endpoint, bind it, then start listening.
|
|
var wq waiter.Queue
|
|
ep, e := s.NewEndpoint(tcp.ProtocolNumber, proto, &wq)
|
|
if e != nil {
|
|
log.Fatal(e)
|
|
}
|
|
|
|
defer ep.Close()
|
|
|
|
if err := ep.Bind(tcpip.FullAddress{Port: uint16(localPort)}); err != nil {
|
|
log.Fatal("Bind failed: ", err)
|
|
}
|
|
|
|
if err := ep.Listen(10); err != nil {
|
|
log.Fatal("Listen failed: ", err)
|
|
}
|
|
|
|
// Wait for connections to appear.
|
|
waitEntry, notifyCh := waiter.NewChannelEntry(waiter.ReadableEvents)
|
|
wq.EventRegister(&waitEntry)
|
|
defer wq.EventUnregister(&waitEntry)
|
|
|
|
for {
|
|
n, wq, err := ep.Accept(nil)
|
|
if err != nil {
|
|
if _, ok := err.(*tcpip.ErrWouldBlock); ok {
|
|
<-notifyCh
|
|
continue
|
|
}
|
|
|
|
log.Fatal("Accept() failed:", err)
|
|
}
|
|
|
|
go echo(wq, n) // S/R-SAFE: sample code.
|
|
}
|
|
}
|