feature: setup veth and allow routing to an external interface

This commit is contained in:
RouxAntoine 2024-01-02 18:39:09 +01:00
parent f63f74284d
commit fdb4db7dba
Signed by: antoine
GPG Key ID: 098FB66FC0475E70
5 changed files with 159 additions and 9 deletions

View File

@ -33,14 +33,14 @@ func setupEnv() int {
}
}(newNs)
vethPair, err := netlink.NewVethPair(newNs)
vethPair, err := netlink.NewVirtualPairing(newNs, "wlp3s0")
if err != nil {
fmt.Println("new Veth error", err)
return 1
}
defer func(veth *netlink.PairLink) {
err = veth.DeleteLink()
err = veth.DeleteVirtualPairing()
if err != nil {
fmt.Println("delete vethPair error", err)
}

5
go.mod
View File

@ -9,4 +9,7 @@ require (
golang.org/x/sys v0.15.0
)
require github.com/vishvananda/netns v0.0.0-20200728191858-db3c7e526aae // indirect
require (
github.com/coreos/go-iptables v0.7.0 // indirect
github.com/vishvananda/netns v0.0.0-20200728191858-db3c7e526aae // indirect
)

2
go.sum
View File

@ -1,3 +1,5 @@
github.com/coreos/go-iptables v0.7.0 h1:XWM3V+MPRr5/q51NuWSgU0fqMad64Zyxs8ZUoMsamr8=
github.com/coreos/go-iptables v0.7.0/go.mod h1:Qe8Bv2Xik5FyTXwgIbLAnv2sWSBmvWdFETJConOQ//Q=
github.com/docker/docker v24.0.7+incompatible h1:Wo6l37AuwP3JaMnZa226lzVXGA3F9Ig1seQen0cKYlM=
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=

View File

@ -2,9 +2,12 @@ package netlink
import (
"antoine-roux.tk/projects/go/firecracker-netns/internal/netns"
"antoine-roux.tk/projects/go/firecracker-netns/internal/sysctl"
"crypto/rand"
"fmt"
"github.com/coreos/go-iptables/iptables"
"github.com/vishvananda/netlink"
"net"
)
// inspired by https://github.com/containernetworking/plugins/blob/main/pkg/ip/link_linux.go#L57
@ -12,14 +15,16 @@ import (
type Peer struct {
link netlink.Link
netNs netns.NsHandle
ip net.IPNet
}
type PairLink struct {
host Peer
guest Peer
host Peer
guest Peer
wanLinkName string
}
func NewVethPair(ns netns.NsHandle) (pair *PairLink, error error) {
func NewVirtualPairing(ns netns.NsHandle, wanLinkName string) (pair *PairLink, error error) {
origins, err := netns.Get()
if err != nil {
@ -61,6 +66,35 @@ func NewVethPair(ns netns.NsHandle) (pair *PairLink, error error) {
return nil, err
}
err = netlink.LinkSetUp(veth2)
if err != nil {
return nil, err
}
veth2IP := &netlink.Addr{
IPNet: &net.IPNet{
IP: net.IPv4(192, 168, 0, 3),
Mask: net.CIDRMask(24, 32),
},
}
// ip addr add 192.168.0.3/24 dev veth527f8d3e
err = netlink.AddrAdd(veth2, veth2IP)
if err != nil {
return nil, err
}
// ip route add default via veth527f8d3e
_, defaultDst, _ := net.ParseCIDR("0.0.0.0/0")
defaultRoute := &netlink.Route{
LinkIndex: veth2.Attrs().Index,
Dst: defaultDst,
}
err = netlink.RouteAdd(defaultRoute)
if err != nil {
return nil, err
}
err = netns.Set(origins)
if err != nil {
return nil, err
@ -73,11 +107,62 @@ func NewVethPair(ns netns.NsHandle) (pair *PairLink, error error) {
return nil, err
}
return &PairLink{host: Peer{veth1, origins}, guest: Peer{veth2, ns}}, nil
err = netlink.LinkSetUp(veth1)
if err != nil {
return nil, err
}
veth1IP := &netlink.Addr{
IPNet: &net.IPNet{
IP: net.IPv4(192, 168, 0, 4),
Mask: net.CIDRMask(24, 32),
},
}
// ip addr add 192.168.0.4/24 dev vethda43d466
err = netlink.AddrAdd(veth1, veth1IP)
if err != nil {
return nil, err
}
// bash -c 'echo 1 > /proc/sys/net/ipv4/conf/veth4bbc9fef/proxy_arp'
err = sysctl.Set(fmt.Sprintf("net.ipv4.conf.%s.proxy_arp", veth1.Attrs().Name), "1")
if err != nil {
return nil, err
}
ipt, err := iptables.New()
if err != nil {
return nil, err
}
// sudo iptables -t nat -I POSTROUTING 1 -o wlp3s0 -j MASQUERADE --source 192.168.0.3/24
err = ipt.InsertUnique("nat", "POSTROUTING", 1, "-o", wanLinkName, "-j", "MASQUERADE", "--source", veth2IP.IPNet.String())
if err != nil {
return nil, err
}
// sudo iptables -I FORWARD 1 -o vethee574a50 -i wlp3s0 -j ACCEPT
err = ipt.InsertUnique("filter", "FORWARD", 1, "-o", veth1.Attrs().Name, "-i", wanLinkName, "-j", "ACCEPT")
if err != nil {
return nil, err
}
// sudo iptables -I FORWARD 1 -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
err = ipt.InsertUnique("filter", "FORWARD", 1, "-m", "conntrack", "--ctstate", "RELATED,ESTABLISHED", "-j", "ACCEPT")
if err != nil {
return nil, err
}
return &PairLink{
host: Peer{veth1, origins, *veth1IP.IPNet},
guest: Peer{veth2, ns, *veth2IP.IPNet},
wanLinkName: wanLinkName,
}, nil
}
// DeleteLink removes an interface link.
func (p *PairLink) DeleteLink() error {
// DeleteVirtualPairing removes an interface link.
func (p *PairLink) DeleteVirtualPairing() 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)
@ -87,6 +172,29 @@ func (p *PairLink) DeleteLink() error {
return fmt.Errorf("failed to delete %q: %v", p.host.link.Attrs().Name, err)
}
ipt, err := iptables.New()
if err != nil {
return err
}
// sudo iptables -t nat -I POSTROUTING 1 -o wlp3s0 -j MASQUERADE --source 192.168.0.3/24
err = ipt.DeleteIfExists("nat", "POSTROUTING", "-o", p.wanLinkName, "-j", "MASQUERADE", "--source", p.guest.ip.String())
if err != nil {
return err
}
// sudo iptables -I FORWARD 1 -o vethee574a50 -i wlp3s0 -j ACCEPT
err = ipt.DeleteIfExists("filter", "FORWARD", "-o", p.host.link.Attrs().Name, "-i", p.wanLinkName, "-j", "ACCEPT")
if err != nil {
return err
}
// sudo iptables -I FORWARD 1 -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
err = ipt.DeleteIfExists("filter", "FORWARD", "-m", "conntrack", "--ctstate", "RELATED,ESTABLISHED", "-j", "ACCEPT")
if err != nil {
return err
}
return nil
}

37
internal/sysctl/sysctl.go Normal file
View File

@ -0,0 +1,37 @@
package sysctl
// inspired from https://github.com/lorenzosaino/go-sysctl/blob/main/sysctl.go
import (
"os"
"path/filepath"
"strings"
)
const defaultPath = "/proc/sys/"
// Get returns a sysctl from a given key.
func Get(key string) (string, error) {
return readFile(pathFromKey(key))
}
// Set updates the value of a sysctl.
func Set(key, value string) error {
return writeFile(pathFromKey(key), value)
}
func pathFromKey(key string) string {
return filepath.Join(defaultPath, strings.Replace(key, ".", "/", -1))
}
func readFile(path string) (string, error) {
data, err := os.ReadFile(path)
if err != nil {
return "", err
}
return strings.TrimSpace(string(data)), nil
}
func writeFile(path, value string) error {
return os.WriteFile(path, []byte(value), 0o644)
}