mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
Implement veth devices
The veth devices are virtual Ethernet devices. They can act as tunnels between network namespaces to create a bridge to a physical network device in another namespace, but can also be used as standalone network devices. More information can be found here: https://man7.org/linux/man-pages/man4/veth.4.html PiperOrigin-RevId: 638853289
This commit is contained in:
@@ -168,6 +168,21 @@ const (
|
||||
IFLA_GSO_MAX_SIZE = 41
|
||||
)
|
||||
|
||||
// Interface link info attributes, from uapi/linux/if_link.h.
|
||||
const (
|
||||
IFLA_INFO_UNSPEC = 0
|
||||
IFLA_INFO_KIND = 1
|
||||
IFLA_INFO_DATA = 2
|
||||
IFLA_INFO_XSTATS = 3
|
||||
IFLA_INFO_SLAVE_KIND = 4
|
||||
IFLA_INFO_SLAVE_DATA = 5
|
||||
)
|
||||
|
||||
// Virtuall ethernet attributes, from uapi/linux/veth.h.
|
||||
const (
|
||||
VETH_INFO_PEER = 1
|
||||
)
|
||||
|
||||
// InterfaceAddrMessage is struct ifaddrmsg, from uapi/linux/if_addr.h.
|
||||
//
|
||||
// +marshal
|
||||
|
||||
@@ -24,6 +24,8 @@ type contextID int
|
||||
const (
|
||||
// CtxStack is a Context.Value key for a network stack.
|
||||
CtxStack contextID = iota
|
||||
// CtxNamespaceByFD is a Context.Value key for NamespaceByFD.
|
||||
CtxNamespaceByFD
|
||||
)
|
||||
|
||||
// StackFromContext returns the network stack associated with ctx.
|
||||
@@ -33,3 +35,16 @@ func StackFromContext(ctx context.Context) Stack {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// NamespaceByFD returns the network namespace associated with the specified
|
||||
// file descriptor.
|
||||
type NamespaceByFD = func(fd int32) (*Namespace, error)
|
||||
|
||||
// NamespaceByFDFromContext returns NamespaceByFD to lookup the network
|
||||
// namespace associated with the specified file descriptor.
|
||||
func NamespaceByFDFromContext(ctx context.Context) NamespaceByFD {
|
||||
if v := ctx.Value(CtxNamespaceByFD); v != nil {
|
||||
return v.(NamespaceByFD)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -247,3 +247,25 @@ const (
|
||||
TCP_RACK_STATIC_REO_WND
|
||||
TCP_RACK_NO_DUPTHRESH
|
||||
)
|
||||
|
||||
// InterfaceRequest contains information about an adding interface.
|
||||
type InterfaceRequest struct {
|
||||
// Kind is the link type.
|
||||
Kind string
|
||||
// Name is the interface name.
|
||||
Name string
|
||||
// Addr is the hardware device address.
|
||||
Addr []byte
|
||||
// MTU is the maximum transmission unit.
|
||||
MTU uint32
|
||||
// Data is link type specific device properties.
|
||||
Data any
|
||||
}
|
||||
|
||||
// VethPeerReq contains information about a second interface of a new veth pair.
|
||||
type VethPeerReq struct {
|
||||
// Req is information about the second end of the new veth pair.
|
||||
Req InterfaceRequest
|
||||
// Stack is the stack where the second end has to be added.
|
||||
Stack Stack
|
||||
}
|
||||
|
||||
@@ -108,6 +108,8 @@ func (t *Task) contextValue(key any, isTaskGoroutine bool) any {
|
||||
return t.k.GetDevGoferClient(t.k.ContainerName(t.containerID))
|
||||
case inet.CtxStack:
|
||||
return t.NetworkContext()
|
||||
case inet.CtxNamespaceByFD:
|
||||
return t.NetworkNamespaceByFD
|
||||
case ktime.CtxRealtimeClock:
|
||||
return t.k.RealtimeClock()
|
||||
case limits.CtxLimits:
|
||||
|
||||
@@ -15,6 +15,9 @@
|
||||
package kernel
|
||||
|
||||
import (
|
||||
"gvisor.dev/gvisor/pkg/errors/linuxerr"
|
||||
"gvisor.dev/gvisor/pkg/sentry/fsimpl/kernfs"
|
||||
"gvisor.dev/gvisor/pkg/sentry/fsimpl/nsfs"
|
||||
"gvisor.dev/gvisor/pkg/sentry/inet"
|
||||
)
|
||||
|
||||
@@ -50,3 +53,27 @@ func (t *Task) GetNetworkNamespace() *inet.Namespace {
|
||||
t.mu.Unlock()
|
||||
return netns
|
||||
}
|
||||
|
||||
// NetworkNamespaceByFD returns the network namespace associated with the specified descriptor.
|
||||
func (t *Task) NetworkNamespaceByFD(fd int32) (*inet.Namespace, error) {
|
||||
file := t.GetFile(fd)
|
||||
if file == nil {
|
||||
return nil, linuxerr.EBADF
|
||||
}
|
||||
defer file.DecRef(t)
|
||||
|
||||
d, ok := file.Dentry().Impl().(*kernfs.Dentry)
|
||||
if !ok {
|
||||
return nil, linuxerr.EINVAL
|
||||
}
|
||||
i, ok := d.Inode().(*nsfs.Inode)
|
||||
if !ok {
|
||||
return nil, linuxerr.EINVAL
|
||||
}
|
||||
ns, ok := i.Namespace().(*inet.Namespace)
|
||||
if !ok {
|
||||
return nil, linuxerr.EINVAL
|
||||
}
|
||||
ns.IncRef()
|
||||
return ns, nil
|
||||
}
|
||||
|
||||
@@ -45,7 +45,10 @@ go_library(
|
||||
"//pkg/syserr",
|
||||
"//pkg/tcpip",
|
||||
"//pkg/tcpip/header",
|
||||
"//pkg/tcpip/link/ethernet",
|
||||
"//pkg/tcpip/link/packetsocket",
|
||||
"//pkg/tcpip/link/tun",
|
||||
"//pkg/tcpip/link/veth",
|
||||
"//pkg/tcpip/network/ipv4",
|
||||
"//pkg/tcpip/network/ipv6",
|
||||
"//pkg/tcpip/stack",
|
||||
|
||||
@@ -27,6 +27,9 @@ import (
|
||||
"gvisor.dev/gvisor/pkg/syserr"
|
||||
"gvisor.dev/gvisor/pkg/tcpip"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/header"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/link/ethernet"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/link/packetsocket"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/link/veth"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/network/ipv4"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/network/ipv6"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/stack"
|
||||
@@ -104,18 +107,18 @@ func (s *Stack) RemoveInterface(idx int32) error {
|
||||
// SetInterface implements inet.Stack.SetInterface.
|
||||
func (s *Stack) SetInterface(ctx context.Context, msg *nlmsg.Message) *syserr.Error {
|
||||
var ifinfomsg linux.InterfaceInfoMessage
|
||||
attrs, ok := msg.GetData(&ifinfomsg)
|
||||
attrsView, ok := msg.GetData(&ifinfomsg)
|
||||
if !ok {
|
||||
return syserr.ErrInvalidArgument
|
||||
}
|
||||
for !attrs.Empty() {
|
||||
// The index is unspecified, search by the interface name.
|
||||
ahdr, value, rest, ok := attrs.ParseFirst()
|
||||
if !ok {
|
||||
return syserr.ErrInvalidArgument
|
||||
}
|
||||
attrs = rest
|
||||
switch ahdr.Type {
|
||||
attrs, ok := attrsView.Parse()
|
||||
if !ok {
|
||||
return syserr.ErrInvalidArgument
|
||||
}
|
||||
ifname := ""
|
||||
for attr := range attrs {
|
||||
value := attrs[attr]
|
||||
switch attr {
|
||||
case linux.IFLA_IFNAME:
|
||||
if len(value) < 1 {
|
||||
return syserr.ErrInvalidArgument
|
||||
@@ -124,23 +127,28 @@ func (s *Stack) SetInterface(ctx context.Context, msg *nlmsg.Message) *syserr.Er
|
||||
// Device name changing isn't supported yet.
|
||||
return syserr.ErrNotSupported
|
||||
}
|
||||
ifname := string(value[:len(value)-1])
|
||||
ifname = value.String()
|
||||
for idx, ifa := range s.Interfaces() {
|
||||
if ifname == ifa.Name {
|
||||
ifinfomsg.Index = idx
|
||||
break
|
||||
}
|
||||
}
|
||||
case linux.IFLA_MASTER:
|
||||
case linux.IFLA_LINKINFO:
|
||||
default:
|
||||
ctx.Warningf("unexpected attribute: %x", ahdr.Type)
|
||||
ctx.Warningf("unexpected attribute: %x", attr)
|
||||
return syserr.ErrNotSupported
|
||||
}
|
||||
}
|
||||
flags := msg.Header().Flags
|
||||
if ifinfomsg.Index == 0 {
|
||||
if flags&linux.NLM_F_CREATE != 0 {
|
||||
return s.newInterface(ctx, msg, attrs)
|
||||
}
|
||||
return syserr.ErrNoDevice
|
||||
}
|
||||
|
||||
flags := msg.Header().Flags
|
||||
if flags&(linux.NLM_F_EXCL|linux.NLM_F_REPLACE) != 0 {
|
||||
return syserr.ErrExists
|
||||
}
|
||||
@@ -156,9 +164,123 @@ func (s *Stack) SetInterface(ctx context.Context, msg *nlmsg.Message) *syserr.Er
|
||||
}
|
||||
// Netstack interfaces are always up.
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
const defaultMTU = 1500
|
||||
|
||||
func (s *Stack) newVeth(ctx context.Context, linkAttrs map[uint16]nlmsg.BytesView, linkInfoAttrs map[uint16]nlmsg.BytesView) *syserr.Error {
|
||||
var (
|
||||
linkInfoData map[uint16]nlmsg.BytesView
|
||||
ifinfomsg linux.InterfaceInfoMessage
|
||||
peerLinkAttrs map[uint16]nlmsg.BytesView
|
||||
)
|
||||
|
||||
peerStack := s
|
||||
peerName := ""
|
||||
ifname := ""
|
||||
|
||||
if v, ok := linkAttrs[linux.IFLA_IFNAME]; ok {
|
||||
ifname = v.String()
|
||||
}
|
||||
if value, ok := linkInfoAttrs[linux.IFLA_INFO_DATA]; ok {
|
||||
linkInfoData, ok = nlmsg.AttrsView(value).Parse()
|
||||
if !ok {
|
||||
return syserr.ErrInvalidArgument
|
||||
}
|
||||
if v, ok := linkInfoData[linux.VETH_INFO_PEER]; ok {
|
||||
attrsView := nlmsg.AttrsView(v[ifinfomsg.SizeBytes():])
|
||||
if !ok {
|
||||
return syserr.ErrInvalidArgument
|
||||
}
|
||||
peerLinkAttrs, ok = attrsView.Parse()
|
||||
if !ok {
|
||||
return syserr.ErrInvalidArgument
|
||||
}
|
||||
if v, ok = peerLinkAttrs[linux.IFLA_IFNAME]; ok {
|
||||
peerName = v.String()
|
||||
}
|
||||
if v, ok = peerLinkAttrs[linux.IFLA_NET_NS_FD]; ok {
|
||||
fd, ok := v.Uint32()
|
||||
if !ok {
|
||||
return syserr.ErrInvalidArgument
|
||||
}
|
||||
f := inet.NamespaceByFDFromContext(ctx)
|
||||
if f == nil {
|
||||
return syserr.ErrInvalidArgument
|
||||
}
|
||||
ns, err := f(int32(fd))
|
||||
if err != nil {
|
||||
return syserr.FromError(err)
|
||||
}
|
||||
defer ns.DecRef(ctx)
|
||||
peerStack = ns.Stack().(*Stack)
|
||||
}
|
||||
}
|
||||
}
|
||||
ep, peerEP := veth.NewPair(defaultMTU)
|
||||
id := tcpip.NICID(s.Stack.UniqueID())
|
||||
peerID := tcpip.NICID(peerStack.Stack.UniqueID())
|
||||
if ifname == "" {
|
||||
ifname = fmt.Sprintf("veth%d", id)
|
||||
}
|
||||
err := s.Stack.CreateNICWithOptions(id, packetsocket.New(ethernet.New(ep)), stack.NICOptions{
|
||||
Name: ifname,
|
||||
})
|
||||
if err != nil {
|
||||
return syserr.TranslateNetstackError(err)
|
||||
}
|
||||
ep.SetStack(s.Stack, id)
|
||||
|
||||
if peerName == "" {
|
||||
peerName = fmt.Sprintf("veth%d", peerID)
|
||||
}
|
||||
err = peerStack.Stack.CreateNICWithOptions(peerID, packetsocket.New(ethernet.New(peerEP)), stack.NICOptions{
|
||||
Name: peerName,
|
||||
})
|
||||
if err != nil {
|
||||
peerEP.Close()
|
||||
return syserr.TranslateNetstackError(err)
|
||||
}
|
||||
peerEP.SetStack(peerStack.Stack, id)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Stack) newInterface(ctx context.Context, msg *nlmsg.Message, linkAttrs map[uint16]nlmsg.BytesView) *syserr.Error {
|
||||
var (
|
||||
linkInfoAttrs map[uint16]nlmsg.BytesView
|
||||
kind string
|
||||
)
|
||||
|
||||
if v, ok := linkAttrs[linux.IFLA_LINKINFO]; ok {
|
||||
linkInfoAttrs, ok = nlmsg.AttrsView(v).Parse()
|
||||
if !ok {
|
||||
return syserr.ErrInvalidArgument
|
||||
}
|
||||
|
||||
for attr := range linkInfoAttrs {
|
||||
value := linkInfoAttrs[attr]
|
||||
switch attr {
|
||||
case linux.IFLA_INFO_KIND:
|
||||
kind = value.String()
|
||||
case linux.IFLA_INFO_DATA:
|
||||
default:
|
||||
ctx.Warningf("unexpected link info attribute: %x", attr)
|
||||
return syserr.ErrNotSupported
|
||||
}
|
||||
}
|
||||
}
|
||||
switch kind {
|
||||
case "":
|
||||
return syserr.ErrInvalidArgument
|
||||
case "veth":
|
||||
return s.newVeth(ctx, linkAttrs, linkInfoAttrs)
|
||||
}
|
||||
return syserr.ErrNotSupported
|
||||
}
|
||||
|
||||
// InterfaceAddrs implements inet.Stack.InterfaceAddrs.
|
||||
func (s *Stack) InterfaceAddrs() map[int32][]inet.InterfaceAddr {
|
||||
nicAddrs := make(map[int32][]inet.InterfaceAddr)
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
load("//tools:defs.bzl", "go_library")
|
||||
|
||||
package(
|
||||
default_applicable_licenses = ["//:license"],
|
||||
licenses = ["notice"],
|
||||
)
|
||||
|
||||
go_library(
|
||||
name = "veth",
|
||||
srcs = ["veth.go"],
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
"//pkg/sync",
|
||||
"//pkg/tcpip",
|
||||
"//pkg/tcpip/header",
|
||||
"//pkg/tcpip/stack",
|
||||
],
|
||||
)
|
||||
@@ -0,0 +1,207 @@
|
||||
// Copyright 2024 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.
|
||||
|
||||
// Package veth provides the implementation of virtual ethernet device pair.
|
||||
package veth
|
||||
|
||||
import (
|
||||
"gvisor.dev/gvisor/pkg/sync"
|
||||
"gvisor.dev/gvisor/pkg/tcpip"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/header"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/stack"
|
||||
)
|
||||
|
||||
var _ stack.LinkEndpoint = (*Endpoint)(nil)
|
||||
var _ stack.GSOEndpoint = (*Endpoint)(nil)
|
||||
|
||||
// +stateify savable
|
||||
type vethPacket struct {
|
||||
e *Endpoint
|
||||
protocol tcpip.NetworkProtocolNumber
|
||||
pkt *stack.PacketBuffer
|
||||
}
|
||||
|
||||
const backlogQueueSize = 64
|
||||
|
||||
// Endpoint is link layer endpoint that redirects packets to a pair veth endpoint.
|
||||
//
|
||||
// +stateify savable
|
||||
type Endpoint struct {
|
||||
pair *Endpoint
|
||||
mtu uint32
|
||||
|
||||
backlogQueue *chan vethPacket
|
||||
|
||||
// linkAddr is the local address of this endpoint.
|
||||
// linkaddr is immutable.
|
||||
linkAddr tcpip.LinkAddress
|
||||
|
||||
mu sync.RWMutex `state:"nosave"`
|
||||
// +checklocks:mu
|
||||
dispatcher stack.NetworkDispatcher
|
||||
|
||||
// +checklocks:mu
|
||||
stack *stack.Stack
|
||||
// +checklocks:mu
|
||||
idx tcpip.NICID
|
||||
}
|
||||
|
||||
// NewPair creates a new veth pair.
|
||||
func NewPair(mtu uint32) (*Endpoint, *Endpoint) {
|
||||
backlogQueue := make(chan vethPacket, backlogQueueSize)
|
||||
a := &Endpoint{
|
||||
mtu: mtu,
|
||||
linkAddr: tcpip.GetRandMacAddr(),
|
||||
backlogQueue: &backlogQueue,
|
||||
}
|
||||
b := &Endpoint{
|
||||
mtu: mtu,
|
||||
pair: a,
|
||||
linkAddr: tcpip.GetRandMacAddr(),
|
||||
backlogQueue: &backlogQueue,
|
||||
}
|
||||
a.pair = b
|
||||
go func() {
|
||||
for t := range backlogQueue {
|
||||
t.e.InjectInbound(t.protocol, t.pkt)
|
||||
t.pkt.DecRef()
|
||||
}
|
||||
}()
|
||||
return a, b
|
||||
}
|
||||
|
||||
// SetStack stores the stack and the device index.
|
||||
func (e *Endpoint) SetStack(s *stack.Stack, idx tcpip.NICID) {
|
||||
e.mu.Lock()
|
||||
defer e.mu.Unlock()
|
||||
e.stack = s
|
||||
e.idx = idx
|
||||
}
|
||||
|
||||
// Close closes e. Further packet injections will return an error, and all pending
|
||||
// packets are discarded. Close may be called concurrently with WritePackets.
|
||||
func (e *Endpoint) Close() {
|
||||
e.mu.Lock()
|
||||
stack := e.stack
|
||||
e.stack = nil
|
||||
e.mu.Unlock()
|
||||
if stack == nil {
|
||||
return
|
||||
}
|
||||
|
||||
e = e.pair
|
||||
e.mu.Lock()
|
||||
stack = e.stack
|
||||
idx := e.idx
|
||||
e.stack = nil
|
||||
e.mu.Unlock()
|
||||
if stack != nil {
|
||||
stack.RemoveNIC(idx)
|
||||
}
|
||||
close(*e.backlogQueue)
|
||||
}
|
||||
|
||||
// InjectInbound injects an inbound packet. If the endpoint is not attached, the
|
||||
// packet is not delivered.
|
||||
func (e *Endpoint) InjectInbound(protocol tcpip.NetworkProtocolNumber, pkt *stack.PacketBuffer) {
|
||||
e.mu.RLock()
|
||||
d := e.dispatcher
|
||||
e.mu.RUnlock()
|
||||
if d != nil {
|
||||
d.DeliverNetworkPacket(protocol, pkt)
|
||||
}
|
||||
}
|
||||
|
||||
// Attach saves the stack network-layer dispatcher for use later when packets
|
||||
// are injected.
|
||||
func (e *Endpoint) Attach(dispatcher stack.NetworkDispatcher) {
|
||||
e.mu.Lock()
|
||||
defer e.mu.Unlock()
|
||||
e.dispatcher = dispatcher
|
||||
}
|
||||
|
||||
// IsAttached implements stack.LinkEndpoint.IsAttached.
|
||||
func (e *Endpoint) IsAttached() bool {
|
||||
e.mu.RLock()
|
||||
defer e.mu.RUnlock()
|
||||
return e.dispatcher != nil
|
||||
}
|
||||
|
||||
// MTU implements stack.LinkEndpoint.MTU. It returns the value initialized
|
||||
// during construction.
|
||||
func (e *Endpoint) MTU() uint32 {
|
||||
return e.mtu
|
||||
}
|
||||
|
||||
// Capabilities implements stack.LinkEndpoint.Capabilities.
|
||||
func (e *Endpoint) Capabilities() stack.LinkEndpointCapabilities {
|
||||
return stack.CapabilityRXChecksumOffload | stack.CapabilityTXChecksumOffload | stack.CapabilitySaveRestore
|
||||
}
|
||||
|
||||
// GSOMaxSize implements stack.GSOEndpoint.
|
||||
func (*Endpoint) GSOMaxSize() uint32 {
|
||||
return stack.GVisorGSOMaxSize
|
||||
}
|
||||
|
||||
// SupportedGSO implements stack.GSOEndpoint.
|
||||
func (e *Endpoint) SupportedGSO() stack.SupportedGSO {
|
||||
return stack.GVisorGSOSupported
|
||||
}
|
||||
|
||||
// MaxHeaderLength returns the maximum size of the link layer header. Given it
|
||||
// doesn't have a header, it just returns 0.
|
||||
func (*Endpoint) MaxHeaderLength() uint16 {
|
||||
return 0
|
||||
}
|
||||
|
||||
// LinkAddress returns the link address of this endpoint.
|
||||
func (e *Endpoint) LinkAddress() tcpip.LinkAddress {
|
||||
return e.linkAddr
|
||||
}
|
||||
|
||||
// WritePackets stores outbound packets into the channel.
|
||||
// Multiple concurrent calls are permitted.
|
||||
func (e *Endpoint) WritePackets(pkts stack.PacketBufferList) (int, tcpip.Error) {
|
||||
n := 0
|
||||
for _, pkt := range pkts.AsSlice() {
|
||||
// In order to properly loop back to the inbound side we must create a
|
||||
// fresh packet that only contains the underlying payload with no headers
|
||||
// or struct fields set.
|
||||
newPkt := stack.NewPacketBuffer(stack.PacketBufferOptions{
|
||||
Payload: pkt.ToBuffer(),
|
||||
})
|
||||
(*e.backlogQueue) <- vethPacket{
|
||||
e: e.pair,
|
||||
protocol: pkt.NetworkProtocolNumber,
|
||||
pkt: newPkt,
|
||||
}
|
||||
n++
|
||||
}
|
||||
|
||||
return n, nil
|
||||
}
|
||||
|
||||
// Wait implements stack.LinkEndpoint.Wait.
|
||||
func (*Endpoint) Wait() {}
|
||||
|
||||
// ARPHardwareType implements stack.LinkEndpoint.ARPHardwareType.
|
||||
func (*Endpoint) ARPHardwareType() header.ARPHardwareType {
|
||||
return header.ARPHardwareNone
|
||||
}
|
||||
|
||||
// AddHeader implements stack.LinkEndpoint.AddHeader.
|
||||
func (e *Endpoint) AddHeader(pkt *stack.PacketBuffer) {}
|
||||
|
||||
// ParseHeader implements stack.LinkEndpoint.ParseHeader.
|
||||
func (e *Endpoint) ParseHeader(pkt *stack.PacketBuffer) bool { return true }
|
||||
@@ -35,6 +35,8 @@ import (
|
||||
"io"
|
||||
"math"
|
||||
"math/bits"
|
||||
"math/rand"
|
||||
"net"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
@@ -2710,6 +2712,15 @@ func ParseMACAddress(s string) (LinkAddress, error) {
|
||||
return LinkAddress(addr), nil
|
||||
}
|
||||
|
||||
// GetRandMacAddr returns a mac address that can be used for local virtual devices.
|
||||
func GetRandMacAddr() LinkAddress {
|
||||
mac := make(net.HardwareAddr, 6)
|
||||
rand.Read(mac) // Fill with random data.
|
||||
mac[0] &^= 0x1 // Clear multicast bit.
|
||||
mac[0] |= 0x2 // Set local assignment bit (IEEE802).
|
||||
return LinkAddress(mac)
|
||||
}
|
||||
|
||||
// AddressWithPrefix is an address with its subnet prefix length.
|
||||
//
|
||||
// +stateify savable
|
||||
|
||||
@@ -255,7 +255,7 @@ func (n *Network) CreateLinksAndRoutes(args *CreateLinksAndRoutesArgs, _ *struct
|
||||
|
||||
// Loopback normally appear before other interfaces.
|
||||
for _, link := range args.LoopbackLinks {
|
||||
nicID++
|
||||
nicID = tcpip.NICID(n.Stack.UniqueID())
|
||||
nicids[link.Name] = nicID
|
||||
|
||||
linkEP := ethernet.New(loopback.New())
|
||||
|
||||
@@ -19,6 +19,8 @@
|
||||
#include <linux/if.h>
|
||||
#include <linux/netlink.h>
|
||||
#include <linux/rtnetlink.h>
|
||||
#include <linux/veth.h>
|
||||
#include <string.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
@@ -1282,6 +1284,79 @@ TEST(NetlinkRouteTest, PasscredCreds) {
|
||||
EXPECT_THAT(creds.gid, AnyOf(Eq(0), Eq(65534)));
|
||||
}
|
||||
|
||||
#ifndef NLMSG_TAIL
|
||||
#define NLMSG_TAIL(nmsg) \
|
||||
((struct rtattr*)(((char*)(nmsg)) + NLMSG_ALIGN((nmsg)->nlmsg_len)))
|
||||
#endif
|
||||
|
||||
void addattr(struct nlmsghdr* n, int maxlen, int type, const void* data,
|
||||
int alen) {
|
||||
int len = NLA_HDRLEN + alen;
|
||||
struct rtattr* rta;
|
||||
|
||||
ASSERT_LE(NLMSG_ALIGN(n->nlmsg_len) + RTA_ALIGN(len), maxlen);
|
||||
|
||||
rta = NLMSG_TAIL(n);
|
||||
rta->rta_type = type;
|
||||
rta->rta_len = len;
|
||||
memcpy(RTA_DATA(rta), data, alen);
|
||||
n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + RTA_ALIGN(len);
|
||||
}
|
||||
|
||||
TEST(NetlinkRouteTest, VethAdd) {
|
||||
SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_ADMIN)));
|
||||
SKIP_IF(IsRunningWithHostinet());
|
||||
|
||||
Link loopback_link = ASSERT_NO_ERRNO_AND_VALUE(LoopbackLink());
|
||||
|
||||
FileDescriptor fd =
|
||||
ASSERT_NO_ERRNO_AND_VALUE(NetlinkBoundSocket(NETLINK_ROUTE));
|
||||
|
||||
struct request {
|
||||
struct nlmsghdr hdr;
|
||||
struct ifinfomsg ifm;
|
||||
char buf[1024];
|
||||
};
|
||||
|
||||
struct request req = {};
|
||||
req.hdr.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg));
|
||||
req.hdr.nlmsg_type = RTM_NEWLINK;
|
||||
req.hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK | NLM_F_CREATE;
|
||||
req.hdr.nlmsg_seq = kSeq;
|
||||
req.ifm.ifi_family = AF_UNSPEC;
|
||||
req.ifm.ifi_index = 0;
|
||||
req.ifm.ifi_change = IFF_UP;
|
||||
req.ifm.ifi_flags = IFF_UP;
|
||||
|
||||
const char veth_first[] = "veth_first";
|
||||
addattr(&req.hdr, sizeof(req), IFLA_IFNAME, veth_first, strlen(veth_first));
|
||||
|
||||
struct rtattr* linkinfo;
|
||||
linkinfo = NLMSG_TAIL(&req.hdr);
|
||||
{
|
||||
addattr(&req.hdr, sizeof(req), IFLA_LINKINFO, nullptr, 0);
|
||||
addattr(&req.hdr, sizeof(req), IFLA_INFO_KIND, "veth", 4);
|
||||
|
||||
struct rtattr *veth_data, *peer_data;
|
||||
veth_data = NLMSG_TAIL(&req.hdr);
|
||||
{
|
||||
addattr(&req.hdr, sizeof(req), IFLA_INFO_DATA, NULL, 0);
|
||||
peer_data = NLMSG_TAIL(&req.hdr);
|
||||
{
|
||||
struct ifinfomsg ifm = {};
|
||||
addattr(&req.hdr, sizeof(req), VETH_INFO_PEER, &ifm, sizeof(ifm));
|
||||
const char veth_second[] = "veth_second";
|
||||
addattr(&req.hdr, sizeof(req), IFLA_IFNAME, veth_second,
|
||||
strlen(veth_second));
|
||||
}
|
||||
peer_data->rta_len = (uint64_t)NLMSG_TAIL(&req.hdr) - (uint64_t)peer_data;
|
||||
}
|
||||
veth_data->rta_len = (uint64_t)NLMSG_TAIL(&req.hdr) - (uint64_t)veth_data;
|
||||
}
|
||||
linkinfo->rta_len = (uint64_t)NLMSG_TAIL(&req.hdr) - (uint64_t)linkinfo;
|
||||
EXPECT_NO_ERRNO(NetlinkRequestAckOrError(fd, kSeq, &req, req.hdr.nlmsg_len));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
} // namespace testing
|
||||
|
||||
Reference in New Issue
Block a user