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:
Andrei Vagin
2024-05-30 18:25:30 -07:00
committed by gVisor bot
parent 8c3abba800
commit 04e7902bb5
12 changed files with 530 additions and 13 deletions
+3
View File
@@ -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",
+134 -12
View File
@@ -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)