218 lines
5.1 KiB
Go
218 lines
5.1 KiB
Go
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
|
|
|
|
type Peer struct {
|
|
link netlink.Link
|
|
netNs netns.NsHandle
|
|
ip net.IPNet
|
|
}
|
|
|
|
type PairLink struct {
|
|
host Peer
|
|
guest Peer
|
|
wanLinkName string
|
|
}
|
|
|
|
func NewVirtualPairing(ns netns.NsHandle, wanLinkName string) (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 = 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
|
|
}
|
|
|
|
fmt.Println(peerName)
|
|
veth1, err := netlink.LinkByName(peerName)
|
|
if err != nil {
|
|
netlink.LinkDel(veth)
|
|
return nil, err
|
|
}
|
|
|
|
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
|
|
}
|
|
|
|
// 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
|
|
}
|
|
|
|
// echo 1 > /proc/sys/net/ipv4/ip_forward
|
|
err = sysctl.Set("net.ipv4.ip_forward", "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
|
|
}
|
|
|
|
// 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)
|
|
}
|
|
|
|
if err := netlink.LinkDel(p.host.link); err != nil {
|
|
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
|
|
}
|
|
|
|
// 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
|
|
}
|