feature: create veth link between host and network namespace
This commit is contained in:
parent
f58fc5b102
commit
f63f74284d
55
cmd/main.go
55
cmd/main.go
@ -3,6 +3,7 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"antoine-roux.tk/projects/go/firecracker-netns/internal/netlink"
|
||||
"antoine-roux.tk/projects/go/firecracker-netns/internal/netns"
|
||||
"fmt"
|
||||
"net"
|
||||
@ -11,26 +12,43 @@ import (
|
||||
"runtime"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// Lock the OS Thread, so we don't accidentally switch namespaces
|
||||
runtime.LockOSThread()
|
||||
defer runtime.UnlockOSThread()
|
||||
|
||||
handle, err := netns.New()
|
||||
func setupEnv() int {
|
||||
newNs, err := netns.New()
|
||||
if err != nil {
|
||||
fmt.Println("new ns error", err)
|
||||
return 1
|
||||
}
|
||||
|
||||
defer func(handle *netns.NsHandle) {
|
||||
defer func(handle netns.NsHandle) {
|
||||
err := handle.Close()
|
||||
if err != nil {
|
||||
fmt.Println("close ns error", err)
|
||||
}
|
||||
}(&handle)
|
||||
}(newNs)
|
||||
|
||||
err = netns.Set(handle)
|
||||
defer func(ns netns.NsHandle) {
|
||||
err := netns.Delete(ns)
|
||||
if err != nil {
|
||||
fmt.Println("delete ns error", err)
|
||||
}
|
||||
}(newNs)
|
||||
|
||||
vethPair, err := netlink.NewVethPair(newNs)
|
||||
if err != nil {
|
||||
fmt.Println("set ns error", err)
|
||||
fmt.Println("new Veth error", err)
|
||||
return 1
|
||||
}
|
||||
|
||||
defer func(veth *netlink.PairLink) {
|
||||
err = veth.DeleteLink()
|
||||
if err != nil {
|
||||
fmt.Println("delete vethPair error", err)
|
||||
}
|
||||
}(vethPair)
|
||||
|
||||
err = netns.Set(newNs)
|
||||
if err != nil {
|
||||
return 1
|
||||
}
|
||||
|
||||
// Do something with the network namespace
|
||||
@ -45,17 +63,18 @@ func main() {
|
||||
|
||||
cmd.Env = []string{"PS1=-[ns-process]- # "}
|
||||
|
||||
/* cmd.SysProcAttr = &syscall.SysProcAttr{
|
||||
Cloneflags: syscall.CLONE_NEWUTS,
|
||||
}*/
|
||||
|
||||
if err := cmd.Run(); err != nil {
|
||||
fmt.Printf("Error running the /bin/sh command - %s\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
err = netns.Delete(handle)
|
||||
if err != nil {
|
||||
fmt.Println("delete ns error", err)
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func main() {
|
||||
// Lock the OS Thread, so we don't accidentally switch namespaces
|
||||
runtime.LockOSThread()
|
||||
defer runtime.UnlockOSThread()
|
||||
|
||||
os.Exit(setupEnv())
|
||||
}
|
||||
|
3
go.mod
3
go.mod
@ -5,5 +5,8 @@ go 1.21.5
|
||||
require (
|
||||
github.com/docker/docker v24.0.7+incompatible
|
||||
github.com/hashicorp/go-version v1.6.0
|
||||
github.com/vishvananda/netlink v1.2.1-beta.2
|
||||
golang.org/x/sys v0.15.0
|
||||
)
|
||||
|
||||
require github.com/vishvananda/netns v0.0.0-20200728191858-db3c7e526aae // indirect
|
||||
|
6
go.sum
6
go.sum
@ -2,5 +2,11 @@ github.com/docker/docker v24.0.7+incompatible h1:Wo6l37AuwP3JaMnZa226lzVXGA3F9Ig
|
||||
github.com/docker/docker v24.0.7+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
||||
github.com/hashicorp/go-version v1.6.0 h1:feTTfFNnjP967rlCxM/I9g701jU+RN74YKx2mOkIeek=
|
||||
github.com/hashicorp/go-version v1.6.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
|
||||
github.com/vishvananda/netlink v1.2.1-beta.2 h1:Llsql0lnQEbHj0I1OuKyp8otXp0r3q0mPkuhwHfStVs=
|
||||
github.com/vishvananda/netlink v1.2.1-beta.2/go.mod h1:twkDnbuQxJYemMlGd4JFIcuhgX83tXhKS2B/PRMpOho=
|
||||
github.com/vishvananda/netns v0.0.0-20200728191858-db3c7e526aae h1:4hwBBUfQCFe3Cym0ZtKyq7L16eZUtYKs+BaHDN6mAns=
|
||||
github.com/vishvananda/netns v0.0.0-20200728191858-db3c7e526aae/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0=
|
||||
golang.org/x/sys v0.0.0-20200217220822-9197077df867/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200728102440-3e129f6d46b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc=
|
||||
golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
|
103
internal/netlink/netlink.go
Normal file
103
internal/netlink/netlink.go
Normal file
@ -0,0 +1,103 @@
|
||||
package netlink
|
||||
|
||||
import (
|
||||
"antoine-roux.tk/projects/go/firecracker-netns/internal/netns"
|
||||
"crypto/rand"
|
||||
"fmt"
|
||||
"github.com/vishvananda/netlink"
|
||||
)
|
||||
|
||||
// inspired by https://github.com/containernetworking/plugins/blob/main/pkg/ip/link_linux.go#L57
|
||||
|
||||
type Peer struct {
|
||||
link netlink.Link
|
||||
netNs netns.NsHandle
|
||||
}
|
||||
|
||||
type PairLink struct {
|
||||
host Peer
|
||||
guest Peer
|
||||
}
|
||||
|
||||
func NewVethPair(ns netns.NsHandle) (pair *PairLink, error error) {
|
||||
|
||||
origins, err := netns.Get()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = netns.Set(ns)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
name, err := randomVethName()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
peerName, err := randomVethName()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
attrs := netlink.NewLinkAttrs()
|
||||
attrs.Name = name
|
||||
veth := &netlink.Veth{
|
||||
LinkAttrs: attrs,
|
||||
PeerName: peerName,
|
||||
PeerNamespace: netlink.NsFd(origins.Fd),
|
||||
}
|
||||
|
||||
if err := netlink.LinkAdd(veth); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Re-fetch the container link to get its creation-time parameters, e.g. index and mac
|
||||
fmt.Println(name)
|
||||
veth2, err := netlink.LinkByName(name)
|
||||
if err != nil {
|
||||
netlink.LinkDel(veth)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = netns.Set(origins)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
fmt.Println(peerName)
|
||||
veth1, err := netlink.LinkByName(peerName)
|
||||
if err != nil {
|
||||
netlink.LinkDel(veth)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &PairLink{host: Peer{veth1, origins}, guest: Peer{veth2, ns}}, nil
|
||||
}
|
||||
|
||||
// DeleteLink removes an interface link.
|
||||
func (p *PairLink) DeleteLink() error {
|
||||
err := netns.Set(p.host.netNs)
|
||||
if err != nil {
|
||||
return fmt.Errorf("set original Ns for deleting veth %q: %v", p.host.link.Attrs().Name, err)
|
||||
}
|
||||
|
||||
if err := netlink.LinkDel(p.host.link); err != nil {
|
||||
return fmt.Errorf("failed to delete %q: %v", p.host.link.Attrs().Name, err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// randomVethName returns string "veth" with random prefix (hashed from entropy)
|
||||
func randomVethName() (string, error) {
|
||||
entropy := make([]byte, 4)
|
||||
_, err := rand.Read(entropy)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to generate random veth name: %v", err)
|
||||
}
|
||||
|
||||
// NetworkManager (recent versions) will ignore veth devices that start with "veth"
|
||||
return fmt.Sprintf("veth%x", entropy), nil
|
||||
}
|
@ -3,16 +3,19 @@ package netns
|
||||
// inspired from https://github.com/vishvananda/netns/blob/master/netns_linux.go#L95
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/docker/docker/pkg/namesgenerator"
|
||||
"golang.org/x/sys/unix"
|
||||
"os"
|
||||
"path"
|
||||
"fmt"
|
||||
"github.com/docker/docker/pkg/namesgenerator"
|
||||
"golang.org/x/sys/unix"
|
||||
"os"
|
||||
"path"
|
||||
)
|
||||
|
||||
// New create and persist a new namespace with random name
|
||||
// New create and persist a new namespace with random Name
|
||||
func New() (handle NsHandle, error error) {
|
||||
origins, _ := Get()
|
||||
origins, err := Get()
|
||||
if err != nil {
|
||||
return None(), err
|
||||
}
|
||||
defer deferClose(&origins, &error)
|
||||
|
||||
if err := unix.Unshare(unix.CLONE_NEWNET); err != nil {
|
||||
@ -26,7 +29,7 @@ func New() (handle NsHandle, error error) {
|
||||
|
||||
err = Set(origins)
|
||||
if err != nil {
|
||||
return NsHandle{fd: 0}, err
|
||||
return NsHandle{Fd: 0}, err
|
||||
}
|
||||
|
||||
err = newNs.persist()
|
||||
@ -47,7 +50,7 @@ func deferClose(origins *NsHandle, e *error) {
|
||||
|
||||
// Delete deletes a named network namespace
|
||||
func Delete(ns NsHandle) error {
|
||||
namedPath := path.Join(bindMountPath, ns.name)
|
||||
namedPath := path.Join(bindMountPath, ns.Name)
|
||||
|
||||
err := unix.Unmount(namedPath, unix.MNT_DETACH)
|
||||
if err != nil {
|
||||
@ -83,5 +86,5 @@ func GetFromPath(path string) (NsHandle, error) {
|
||||
// Set sets the current network namespace to the namespace represented
|
||||
// by NsHandle.
|
||||
func Set(ns NsHandle) error {
|
||||
return unix.Setns(ns.fd, unix.CLONE_NEWNET)
|
||||
return unix.Setns(ns.Fd, unix.CLONE_NEWNET)
|
||||
}
|
||||
|
@ -3,10 +3,10 @@ package netns
|
||||
// inspired from https://github.com/vishvananda/netns/blob/master/nshandle_linux.go#L11
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"golang.org/x/sys/unix"
|
||||
"os"
|
||||
"path"
|
||||
"fmt"
|
||||
"golang.org/x/sys/unix"
|
||||
"os"
|
||||
"path"
|
||||
)
|
||||
|
||||
const bindMountPath = "/run/netns"
|
||||
@ -14,12 +14,12 @@ const bindMountPath = "/run/netns"
|
||||
// NsHandle is a handle to a network namespace. It can be cast directly
|
||||
// to an int and used as a file descriptor.
|
||||
type NsHandle struct {
|
||||
fd int
|
||||
name string
|
||||
Fd int
|
||||
Name string
|
||||
}
|
||||
|
||||
// persist and bind mount net namespace to `/run/netns`
|
||||
func (ns NsHandle) persist() error {
|
||||
func (ns *NsHandle) persist() error {
|
||||
if _, err := os.Stat(bindMountPath); os.IsNotExist(err) {
|
||||
err = os.MkdirAll(bindMountPath, 0o755)
|
||||
if err != nil {
|
||||
@ -27,7 +27,7 @@ func (ns NsHandle) persist() error {
|
||||
}
|
||||
}
|
||||
|
||||
namedPath := path.Join(bindMountPath, ns.name)
|
||||
namedPath := path.Join(bindMountPath, ns.Name)
|
||||
|
||||
f, err := os.OpenFile(namedPath, os.O_CREATE|os.O_EXCL, 0o444)
|
||||
if err != nil {
|
||||
@ -50,50 +50,50 @@ func (ns NsHandle) persist() error {
|
||||
// Equal determines if two network handles refer to the same network
|
||||
// namespace. This is done by comparing the device and inode that the
|
||||
// file descriptors point to.
|
||||
func (ns NsHandle) Equal(other NsHandle) bool {
|
||||
if ns == other {
|
||||
func (ns *NsHandle) Equal(other NsHandle) bool {
|
||||
if *ns == other {
|
||||
return true
|
||||
}
|
||||
var s1, s2 unix.Stat_t
|
||||
if err := unix.Fstat(ns.fd, &s1); err != nil {
|
||||
if err := unix.Fstat(ns.Fd, &s1); err != nil {
|
||||
return false
|
||||
}
|
||||
if err := unix.Fstat(other.fd, &s2); err != nil {
|
||||
if err := unix.Fstat(other.Fd, &s2); err != nil {
|
||||
return false
|
||||
}
|
||||
return (s1.Dev == s2.Dev) && (s1.Ino == s2.Ino)
|
||||
}
|
||||
|
||||
// String shows the file descriptor number and its dev and inode.
|
||||
func (ns NsHandle) String() string {
|
||||
if ns.fd == -1 {
|
||||
func (ns *NsHandle) String() string {
|
||||
if ns.Fd == -1 {
|
||||
return "NS(none)"
|
||||
}
|
||||
var s unix.Stat_t
|
||||
if err := unix.Fstat(ns.fd, &s); err != nil {
|
||||
if err := unix.Fstat(ns.Fd, &s); err != nil {
|
||||
return fmt.Sprintf("NS(%d: unknown)", ns)
|
||||
}
|
||||
return fmt.Sprintf("NS(%d: %d, %d)", ns, s.Dev, s.Ino)
|
||||
}
|
||||
|
||||
// IsOpen returns true if Close() has not been called.
|
||||
func (ns NsHandle) IsOpen() bool {
|
||||
return ns.fd != -1
|
||||
func (ns *NsHandle) IsOpen() bool {
|
||||
return ns.Fd != -1
|
||||
}
|
||||
|
||||
// Close closes the NsHandle and resets its file descriptor to -1.
|
||||
// It is not safe to use an NsHandle after Close() is called.
|
||||
func (ns NsHandle) Close() error {
|
||||
if err := unix.Close(ns.fd); err != nil {
|
||||
func (ns *NsHandle) Close() error {
|
||||
if err := unix.Close(ns.Fd); err != nil {
|
||||
return err
|
||||
}
|
||||
ns.fd = -1
|
||||
ns.Fd = -1
|
||||
return nil
|
||||
}
|
||||
|
||||
// None gets an empty (closed) NsHandle.
|
||||
func None() NsHandle {
|
||||
return NsHandle{
|
||||
fd: -1,
|
||||
Fd: -1,
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user