Volt VMM (Neutron Stardust): source-available under AGPSL v5.0

KVM-based microVMM for the Volt platform:
- Sub-second VM boot times
- Minimal memory footprint
- Landlock LSM + seccomp security
- Virtio device support
- Custom kernel management

Copyright (c) Armored Gates LLC. All rights reserved.
Licensed under AGPSL v5.0
This commit is contained in:
Karl Clinger
2026-03-21 01:04:35 -05:00
commit 40ed108dd5
143 changed files with 50300 additions and 0 deletions

120
networking/README.md Normal file
View File

@@ -0,0 +1,120 @@
# Volt Unified Networking
Shared network infrastructure for Volt VMs and Voltainer containers.
## Architecture
```
┌─────────────────────────────────────────────────────────────────────┐
│ Host (systemd-networkd) │
│ ┌────────────────────────────────────────────────────────────────┐ │
│ │ volt0 (bridge) │ │
│ │ 10.42.0.1/24 │ │
│ │ ┌──────────────────────────────────────────────────────────┐ │ │
│ │ │ Address Pool: 10.42.0.2 - 10.42.0.254 (DHCP or static) │ │ │
│ │ └──────────────────────────────────────────────────────────┘ │ │
│ └────┬──────────┬──────────┬──────────┬──────────┬─────────────┘ │
│ │ │ │ │ │ │
│ ┌────┴────┐┌────┴────┐┌────┴────┐┌────┴────┐┌────┴────┐ │
│ │ tap0 ││ tap1 ││ veth1a ││ veth2a ││ macvtap │ │
│ │ (NovaVM)││ (NovaVM)││(Voltain)││(Voltain)││ (pass) │ │
│ └────┬────┘└────┬────┘└────┬────┘└────┬────┘└────┬────┘ │
│ │ │ │ │ │ │
└───────┼──────────┼──────────┼──────────┼──────────┼───────────────┘
│ │ │ │ │
┌────┴────┐┌────┴────┐┌────┴────┐┌────┴────┐ │
│ VM 1 ││ VM 2 ││Container││Container│ │
│10.42.0.2││10.42.0.3││10.42.0.4││10.42.0.5│ │
└─────────┘└─────────┘└─────────┘└─────────┘ │
┌─────┴─────┐
│ SR-IOV VF │
│ Passthru │
└───────────┘
```
## Network Types
### 1. Bridged (Default)
- VMs connect via TAP devices
- Containers connect via veth pairs
- All on same L2 network
- Full inter-VM and container communication
### 2. Isolated
- Per-workload network namespace
- No external connectivity
- Useful for security sandboxing
### 3. Host-Only
- NAT to host network
- No external inbound (unless port-mapped)
- iptables masquerade
### 4. Macvtap/SR-IOV
- Near-native network performance
- Direct physical NIC access
- For high-throughput workloads
## Components
```
networking/
├── systemd/ # networkd unit files
│ ├── volt0.netdev # Bridge device
│ ├── volt0.network # Bridge network config
│ └── 90-volt-vmm.link # Link settings
├── pkg/ # Go package
│ └── unified/ # Shared network management
├── configs/ # Example configurations
└── README.md
```
## Usage
### Installing systemd units
```bash
sudo cp systemd/*.netdev systemd/*.network /etc/systemd/network/
sudo systemctl restart systemd-networkd
```
### Creating a TAP for Volt VM
```go
import "volt-vmm/networking/pkg/unified"
nm := unified.NewManager("/run/volt-vmm/network")
tap, err := nm.CreateTAP("volt0", "vm-abc123")
// tap.Name = "tap-abc123"
// tap.FD = ready-to-use file descriptor
```
### Creating veth for Voltainer container
```go
veth, err := nm.CreateVeth("volt0", "container-xyz")
// veth.HostEnd = "veth-xyz-h" (in bridge)
// veth.ContainerEnd = "veth-xyz-c" (move to namespace)
```
## IP Address Management (IPAM)
The unified IPAM provides:
- Static allocation from config
- Dynamic allocation from pool
- DHCP server integration (optional)
- Lease persistence
```json
{
"network": "volt0",
"subnet": "10.42.0.0/24",
"gateway": "10.42.0.1",
"pool": {
"start": "10.42.0.2",
"end": "10.42.0.254"
},
"reservations": {
"vm-web": "10.42.0.10",
"container-db": "10.42.0.20"
}
}
```

View File

@@ -0,0 +1,349 @@
package unified
import (
"encoding/binary"
"encoding/json"
"fmt"
"net"
"os"
"path/filepath"
"sync"
"time"
)
// IPAM manages IP address allocation for networks
type IPAM struct {
stateDir string
pools map[string]*Pool
mu sync.RWMutex
}
// Pool represents an IP address pool for a network
type Pool struct {
// Network name
Name string `json:"name"`
// Subnet
Subnet *net.IPNet `json:"subnet"`
// Gateway address
Gateway net.IP `json:"gateway"`
// Pool start (first allocatable address)
Start net.IP `json:"start"`
// Pool end (last allocatable address)
End net.IP `json:"end"`
// Static reservations (workloadID -> IP)
Reservations map[string]net.IP `json:"reservations"`
// Active leases
Leases map[string]*Lease `json:"leases"`
// Free IPs (bitmap for fast allocation)
allocated map[uint32]bool
}
// NewIPAM creates a new IPAM instance
func NewIPAM(stateDir string) (*IPAM, error) {
if err := os.MkdirAll(stateDir, 0755); err != nil {
return nil, fmt.Errorf("create IPAM state dir: %w", err)
}
ipam := &IPAM{
stateDir: stateDir,
pools: make(map[string]*Pool),
}
// Load existing state
if err := ipam.loadState(); err != nil {
// Non-fatal, might be first run
_ = err
}
return ipam, nil
}
// AddPool adds a new IP pool for a network
func (i *IPAM) AddPool(name string, subnet *net.IPNet, gateway net.IP, reservations map[string]net.IP) error {
i.mu.Lock()
defer i.mu.Unlock()
// Calculate pool range
start := nextIP(subnet.IP)
if gateway != nil && gateway.Equal(start) {
start = nextIP(start)
}
// Broadcast address is last in subnet
end := lastIP(subnet)
pool := &Pool{
Name: name,
Subnet: subnet,
Gateway: gateway,
Start: start,
End: end,
Reservations: reservations,
Leases: make(map[string]*Lease),
allocated: make(map[uint32]bool),
}
// Mark gateway as allocated
if gateway != nil {
pool.allocated[ipToUint32(gateway)] = true
}
// Mark reservations as allocated
for _, ip := range reservations {
pool.allocated[ipToUint32(ip)] = true
}
i.pools[name] = pool
return i.saveState()
}
// Allocate allocates an IP address for a workload
func (i *IPAM) Allocate(network, workloadID string, mac net.HardwareAddr) (*Lease, error) {
i.mu.Lock()
defer i.mu.Unlock()
pool, ok := i.pools[network]
if !ok {
return nil, fmt.Errorf("network %s not found", network)
}
// Check if workload already has a lease
if lease, ok := pool.Leases[workloadID]; ok {
return lease, nil
}
// Check for static reservation
if ip, ok := pool.Reservations[workloadID]; ok {
lease := &Lease{
IP: ip,
MAC: mac,
WorkloadID: workloadID,
Start: time.Now(),
Expires: time.Now().Add(365 * 24 * time.Hour), // Long lease for static
Static: true,
}
pool.Leases[workloadID] = lease
pool.allocated[ipToUint32(ip)] = true
_ = i.saveState()
return lease, nil
}
// Find free IP in pool
ip, err := pool.findFreeIP()
if err != nil {
return nil, err
}
lease := &Lease{
IP: ip,
MAC: mac,
WorkloadID: workloadID,
Start: time.Now(),
Expires: time.Now().Add(24 * time.Hour), // Default 24h lease
Static: false,
}
pool.Leases[workloadID] = lease
pool.allocated[ipToUint32(ip)] = true
_ = i.saveState()
return lease, nil
}
// Release releases an IP address allocation
func (i *IPAM) Release(network, workloadID string) error {
i.mu.Lock()
defer i.mu.Unlock()
pool, ok := i.pools[network]
if !ok {
return nil // Network doesn't exist, nothing to release
}
lease, ok := pool.Leases[workloadID]
if !ok {
return nil // No lease, nothing to release
}
// Don't release static reservations from allocated map
if !lease.Static {
delete(pool.allocated, ipToUint32(lease.IP))
}
delete(pool.Leases, workloadID)
return i.saveState()
}
// GetLease returns the current lease for a workload
func (i *IPAM) GetLease(network, workloadID string) (*Lease, error) {
i.mu.RLock()
defer i.mu.RUnlock()
pool, ok := i.pools[network]
if !ok {
return nil, fmt.Errorf("network %s not found", network)
}
lease, ok := pool.Leases[workloadID]
if !ok {
return nil, fmt.Errorf("no lease for %s", workloadID)
}
return lease, nil
}
// ListLeases returns all active leases for a network
func (i *IPAM) ListLeases(network string) ([]*Lease, error) {
i.mu.RLock()
defer i.mu.RUnlock()
pool, ok := i.pools[network]
if !ok {
return nil, fmt.Errorf("network %s not found", network)
}
result := make([]*Lease, 0, len(pool.Leases))
for _, lease := range pool.Leases {
result = append(result, lease)
}
return result, nil
}
// Reserve creates a static IP reservation
func (i *IPAM) Reserve(network, workloadID string, ip net.IP) error {
i.mu.Lock()
defer i.mu.Unlock()
pool, ok := i.pools[network]
if !ok {
return fmt.Errorf("network %s not found", network)
}
// Check if IP is in subnet
if !pool.Subnet.Contains(ip) {
return fmt.Errorf("IP %s not in subnet %s", ip, pool.Subnet)
}
// Check if already allocated
if pool.allocated[ipToUint32(ip)] {
return fmt.Errorf("IP %s already allocated", ip)
}
if pool.Reservations == nil {
pool.Reservations = make(map[string]net.IP)
}
pool.Reservations[workloadID] = ip
pool.allocated[ipToUint32(ip)] = true
return i.saveState()
}
// Unreserve removes a static IP reservation
func (i *IPAM) Unreserve(network, workloadID string) error {
i.mu.Lock()
defer i.mu.Unlock()
pool, ok := i.pools[network]
if !ok {
return nil
}
if ip, ok := pool.Reservations[workloadID]; ok {
delete(pool.allocated, ipToUint32(ip))
delete(pool.Reservations, workloadID)
return i.saveState()
}
return nil
}
// findFreeIP finds the next available IP in the pool
func (p *Pool) findFreeIP() (net.IP, error) {
startUint := ipToUint32(p.Start)
endUint := ipToUint32(p.End)
for ip := startUint; ip <= endUint; ip++ {
if !p.allocated[ip] {
return uint32ToIP(ip), nil
}
}
return nil, fmt.Errorf("no free IPs in pool %s", p.Name)
}
// saveState persists IPAM state to disk
func (i *IPAM) saveState() error {
data, err := json.MarshalIndent(i.pools, "", " ")
if err != nil {
return err
}
return os.WriteFile(filepath.Join(i.stateDir, "pools.json"), data, 0644)
}
// loadState loads IPAM state from disk
func (i *IPAM) loadState() error {
data, err := os.ReadFile(filepath.Join(i.stateDir, "pools.json"))
if err != nil {
return err
}
if err := json.Unmarshal(data, &i.pools); err != nil {
return err
}
// Rebuild allocated maps
for _, pool := range i.pools {
pool.allocated = make(map[uint32]bool)
if pool.Gateway != nil {
pool.allocated[ipToUint32(pool.Gateway)] = true
}
for _, ip := range pool.Reservations {
pool.allocated[ipToUint32(ip)] = true
}
for _, lease := range pool.Leases {
pool.allocated[ipToUint32(lease.IP)] = true
}
}
return nil
}
// Helper functions for IP math
func ipToUint32(ip net.IP) uint32 {
ip = ip.To4()
if ip == nil {
return 0
}
return binary.BigEndian.Uint32(ip)
}
func uint32ToIP(n uint32) net.IP {
ip := make(net.IP, 4)
binary.BigEndian.PutUint32(ip, n)
return ip
}
func nextIP(ip net.IP) net.IP {
return uint32ToIP(ipToUint32(ip) + 1)
}
func lastIP(subnet *net.IPNet) net.IP {
// Get the broadcast address (last IP in subnet)
ip := subnet.IP.To4()
mask := subnet.Mask
broadcast := make(net.IP, 4)
for i := range ip {
broadcast[i] = ip[i] | ^mask[i]
}
// Return one before broadcast (last usable)
return uint32ToIP(ipToUint32(broadcast) - 1)
}

View File

@@ -0,0 +1,537 @@
package unified
import (
"encoding/json"
"fmt"
"net"
"os"
"path/filepath"
"sync"
"time"
"github.com/vishvananda/netlink"
)
// Manager handles unified network operations for VMs and containers
type Manager struct {
// State directory for leases and config
stateDir string
// Network configurations by name
networks map[string]*NetworkConfig
// IPAM state
ipam *IPAM
// Active interfaces by workload ID
interfaces map[string]*Interface
mu sync.RWMutex
}
// NewManager creates a new unified network manager
func NewManager(stateDir string) (*Manager, error) {
if err := os.MkdirAll(stateDir, 0755); err != nil {
return nil, fmt.Errorf("create state dir: %w", err)
}
m := &Manager{
stateDir: stateDir,
networks: make(map[string]*NetworkConfig),
interfaces: make(map[string]*Interface),
}
// Initialize IPAM
ipam, err := NewIPAM(filepath.Join(stateDir, "ipam"))
if err != nil {
return nil, fmt.Errorf("init IPAM: %w", err)
}
m.ipam = ipam
// Load existing state
if err := m.loadState(); err != nil {
// Non-fatal, might be first run
_ = err
}
return m, nil
}
// AddNetwork registers a network configuration
func (m *Manager) AddNetwork(config *NetworkConfig) error {
m.mu.Lock()
defer m.mu.Unlock()
// Validate
if config.Name == "" {
return fmt.Errorf("network name required")
}
if config.Subnet == "" {
return fmt.Errorf("subnet required")
}
_, subnet, err := net.ParseCIDR(config.Subnet)
if err != nil {
return fmt.Errorf("invalid subnet: %w", err)
}
// Set defaults
if config.MTU == 0 {
config.MTU = 1500
}
if config.Type == "" {
config.Type = NetworkBridged
}
if config.Bridge == "" && config.Type == NetworkBridged {
config.Bridge = config.Name
}
// Register with IPAM
if config.IPAM != nil {
var gateway net.IP
if config.Gateway != "" {
gateway = net.ParseIP(config.Gateway)
}
if err := m.ipam.AddPool(config.Name, subnet, gateway, nil); err != nil {
return fmt.Errorf("register IPAM pool: %w", err)
}
}
m.networks[config.Name] = config
return m.saveState()
}
// EnsureBridge ensures the bridge exists and is configured
func (m *Manager) EnsureBridge(name string) (*BridgeInfo, error) {
// Check if bridge exists
link, err := netlink.LinkByName(name)
if err != nil {
// Bridge doesn't exist, create it
bridge := &netlink.Bridge{
LinkAttrs: netlink.LinkAttrs{
Name: name,
MTU: 1500,
},
}
if err := netlink.LinkAdd(bridge); err != nil {
return nil, fmt.Errorf("create bridge %s: %w", name, err)
}
link, err = netlink.LinkByName(name)
if err != nil {
return nil, fmt.Errorf("get created bridge: %w", err)
}
}
// Ensure it's up
if err := netlink.LinkSetUp(link); err != nil {
return nil, fmt.Errorf("set bridge up: %w", err)
}
// Get bridge info
info := &BridgeInfo{
Name: name,
MTU: link.Attrs().MTU,
Up: link.Attrs().OperState == netlink.OperUp,
}
if link.Attrs().HardwareAddr != nil {
info.MAC = link.Attrs().HardwareAddr
}
// Get IP addresses
addrs, err := netlink.AddrList(link, netlink.FAMILY_V4)
if err == nil && len(addrs) > 0 {
info.IP = addrs[0].IP
info.Subnet = addrs[0].IPNet
}
return info, nil
}
// CreateTAP creates a TAP device for a VM and attaches it to the bridge
func (m *Manager) CreateTAP(network, workloadID string) (*Interface, error) {
m.mu.Lock()
defer m.mu.Unlock()
config, ok := m.networks[network]
if !ok {
return nil, fmt.Errorf("network %s not found", network)
}
// Generate TAP name (max 15 chars for Linux interface names)
tapName := fmt.Sprintf("tap-%s", truncateID(workloadID, 10))
// Create TAP device
tap := &netlink.Tuntap{
LinkAttrs: netlink.LinkAttrs{
Name: tapName,
MTU: config.MTU,
},
Mode: netlink.TUNTAP_MODE_TAP,
Flags: netlink.TUNTAP_NO_PI | netlink.TUNTAP_VNET_HDR,
Queues: 1, // Can increase for multi-queue
}
if err := netlink.LinkAdd(tap); err != nil {
return nil, fmt.Errorf("create TAP %s: %w", tapName, err)
}
// Get the created link to get FD
link, err := netlink.LinkByName(tapName)
if err != nil {
_ = netlink.LinkDel(tap)
return nil, fmt.Errorf("get TAP link: %w", err)
}
// Get the file descriptor from the TAP
// This requires opening /dev/net/tun with the TAP name
fd, err := openTAPFD(tapName)
if err != nil {
_ = netlink.LinkDel(tap)
return nil, fmt.Errorf("open TAP fd: %w", err)
}
// Attach to bridge
bridge, err := netlink.LinkByName(config.Bridge)
if err != nil {
_ = netlink.LinkDel(tap)
return nil, fmt.Errorf("get bridge %s: %w", config.Bridge, err)
}
if err := netlink.LinkSetMaster(link, bridge); err != nil {
_ = netlink.LinkDel(tap)
return nil, fmt.Errorf("attach to bridge: %w", err)
}
// Set link up
if err := netlink.LinkSetUp(link); err != nil {
_ = netlink.LinkDel(tap)
return nil, fmt.Errorf("set TAP up: %w", err)
}
// Generate MAC address
mac := generateMAC(workloadID)
// Allocate IP if IPAM enabled
var ip net.IP
var mask net.IPMask
var gateway net.IP
if config.IPAM != nil {
lease, err := m.ipam.Allocate(network, workloadID, mac)
if err != nil {
_ = netlink.LinkDel(tap)
return nil, fmt.Errorf("allocate IP: %w", err)
}
ip = lease.IP
_, subnet, _ := net.ParseCIDR(config.Subnet)
mask = subnet.Mask
if config.Gateway != "" {
gateway = net.ParseIP(config.Gateway)
}
}
iface := &Interface{
Name: tapName,
MAC: mac,
IP: ip,
Mask: mask,
Gateway: gateway,
Bridge: config.Bridge,
WorkloadID: workloadID,
WorkloadType: WorkloadVM,
FD: fd,
}
m.interfaces[workloadID] = iface
_ = m.saveState()
return iface, nil
}
// CreateVeth creates a veth pair for a container and attaches host end to bridge
func (m *Manager) CreateVeth(network, workloadID string) (*Interface, error) {
m.mu.Lock()
defer m.mu.Unlock()
config, ok := m.networks[network]
if !ok {
return nil, fmt.Errorf("network %s not found", network)
}
// Generate veth names (max 15 chars)
hostName := fmt.Sprintf("veth-%s-h", truncateID(workloadID, 7))
peerName := fmt.Sprintf("veth-%s-c", truncateID(workloadID, 7))
// Create veth pair
veth := &netlink.Veth{
LinkAttrs: netlink.LinkAttrs{
Name: hostName,
MTU: config.MTU,
},
PeerName: peerName,
}
if err := netlink.LinkAdd(veth); err != nil {
return nil, fmt.Errorf("create veth pair: %w", err)
}
// Get the created links
hostLink, err := netlink.LinkByName(hostName)
if err != nil {
_ = netlink.LinkDel(veth)
return nil, fmt.Errorf("get host veth: %w", err)
}
peerLink, err := netlink.LinkByName(peerName)
if err != nil {
_ = netlink.LinkDel(veth)
return nil, fmt.Errorf("get peer veth: %w", err)
}
// Attach host end to bridge
bridge, err := netlink.LinkByName(config.Bridge)
if err != nil {
_ = netlink.LinkDel(veth)
return nil, fmt.Errorf("get bridge %s: %w", config.Bridge, err)
}
if err := netlink.LinkSetMaster(hostLink, bridge); err != nil {
_ = netlink.LinkDel(veth)
return nil, fmt.Errorf("attach to bridge: %w", err)
}
// Set host end up
if err := netlink.LinkSetUp(hostLink); err != nil {
_ = netlink.LinkDel(veth)
return nil, fmt.Errorf("set host veth up: %w", err)
}
// Generate MAC address
mac := generateMAC(workloadID)
// Set MAC on peer (container) end
if err := netlink.LinkSetHardwareAddr(peerLink, mac); err != nil {
_ = netlink.LinkDel(veth)
return nil, fmt.Errorf("set peer MAC: %w", err)
}
// Allocate IP if IPAM enabled
var ip net.IP
var mask net.IPMask
var gateway net.IP
if config.IPAM != nil {
lease, err := m.ipam.Allocate(network, workloadID, mac)
if err != nil {
_ = netlink.LinkDel(veth)
return nil, fmt.Errorf("allocate IP: %w", err)
}
ip = lease.IP
_, subnet, _ := net.ParseCIDR(config.Subnet)
mask = subnet.Mask
if config.Gateway != "" {
gateway = net.ParseIP(config.Gateway)
}
}
iface := &Interface{
Name: hostName,
PeerName: peerName,
MAC: mac,
IP: ip,
Mask: mask,
Gateway: gateway,
Bridge: config.Bridge,
WorkloadID: workloadID,
WorkloadType: WorkloadContainer,
}
m.interfaces[workloadID] = iface
_ = m.saveState()
return iface, nil
}
// MoveVethToNamespace moves the container end of a veth pair to a network namespace
func (m *Manager) MoveVethToNamespace(workloadID string, nsFD int) error {
m.mu.RLock()
iface, ok := m.interfaces[workloadID]
m.mu.RUnlock()
if !ok {
return fmt.Errorf("interface for %s not found", workloadID)
}
if iface.PeerName == "" {
return fmt.Errorf("not a veth pair interface")
}
// Get peer link
peerLink, err := netlink.LinkByName(iface.PeerName)
if err != nil {
return fmt.Errorf("get peer veth: %w", err)
}
// Move to namespace
if err := netlink.LinkSetNsFd(peerLink, nsFD); err != nil {
return fmt.Errorf("move to namespace: %w", err)
}
return nil
}
// ConfigureContainerInterface configures the interface inside the container namespace
// This should be called from within the container's network namespace
func (m *Manager) ConfigureContainerInterface(workloadID string) error {
m.mu.RLock()
iface, ok := m.interfaces[workloadID]
m.mu.RUnlock()
if !ok {
return fmt.Errorf("interface for %s not found", workloadID)
}
// Get the interface (should be the peer that was moved into this namespace)
link, err := netlink.LinkByName(iface.PeerName)
if err != nil {
return fmt.Errorf("get interface: %w", err)
}
// Set link up
if err := netlink.LinkSetUp(link); err != nil {
return fmt.Errorf("set link up: %w", err)
}
// Add IP address if allocated
if iface.IP != nil {
addr := &netlink.Addr{
IPNet: &net.IPNet{
IP: iface.IP,
Mask: iface.Mask,
},
}
if err := netlink.AddrAdd(link, addr); err != nil {
return fmt.Errorf("add IP address: %w", err)
}
}
// Add default route via gateway
if iface.Gateway != nil {
route := &netlink.Route{
Gw: iface.Gateway,
}
if err := netlink.RouteAdd(route); err != nil {
return fmt.Errorf("add default route: %w", err)
}
}
return nil
}
// Release releases the network interface for a workload
func (m *Manager) Release(workloadID string) error {
m.mu.Lock()
defer m.mu.Unlock()
iface, ok := m.interfaces[workloadID]
if !ok {
return nil // Already released
}
// Release IP from IPAM
for network := range m.networks {
_ = m.ipam.Release(network, workloadID)
}
// Delete the interface
link, err := netlink.LinkByName(iface.Name)
if err == nil {
_ = netlink.LinkDel(link)
}
delete(m.interfaces, workloadID)
return m.saveState()
}
// GetInterface returns the interface for a workload
func (m *Manager) GetInterface(workloadID string) (*Interface, error) {
m.mu.RLock()
defer m.mu.RUnlock()
iface, ok := m.interfaces[workloadID]
if !ok {
return nil, fmt.Errorf("interface for %s not found", workloadID)
}
return iface, nil
}
// ListInterfaces returns all managed interfaces
func (m *Manager) ListInterfaces() []*Interface {
m.mu.RLock()
defer m.mu.RUnlock()
result := make([]*Interface, 0, len(m.interfaces))
for _, iface := range m.interfaces {
result = append(result, iface)
}
return result
}
// saveState persists current state to disk
func (m *Manager) saveState() error {
data, err := json.MarshalIndent(m.interfaces, "", " ")
if err != nil {
return err
}
return os.WriteFile(filepath.Join(m.stateDir, "interfaces.json"), data, 0644)
}
// loadState loads state from disk
func (m *Manager) loadState() error {
data, err := os.ReadFile(filepath.Join(m.stateDir, "interfaces.json"))
if err != nil {
return err
}
return json.Unmarshal(data, &m.interfaces)
}
// truncateID truncates a workload ID for use in interface names
func truncateID(id string, maxLen int) string {
if len(id) <= maxLen {
return id
}
return id[:maxLen]
}
// generateMAC generates a deterministic MAC address from workload ID
func generateMAC(workloadID string) net.HardwareAddr {
// Use first 5 bytes of workload ID hash
// Set local/unicast bits
mac := make([]byte, 6)
mac[0] = 0x52 // Local, unicast (Volt prefix)
mac[1] = 0x54
mac[2] = 0x00
// Hash-based bytes
h := 0
for _, c := range workloadID {
h = h*31 + int(c)
}
mac[3] = byte((h >> 16) & 0xFF)
mac[4] = byte((h >> 8) & 0xFF)
mac[5] = byte(h & 0xFF)
return mac
}
// openTAPFD opens a TAP device and returns its file descriptor
func openTAPFD(name string) (int, error) {
// This is a simplified version - in production, use proper ioctl
// The netlink library handles TAP creation, but we need the FD for VMM use
// For now, return -1 as placeholder
// Real implementation would:
// 1. Open /dev/net/tun
// 2. ioctl TUNSETIFF with name and flags
// 3. Return the fd
return -1, fmt.Errorf("TAP FD extraction not yet implemented - use device fd from netlink")
}

View File

@@ -0,0 +1,199 @@
// Package unified provides shared networking for Volt VMs and Voltainer containers.
//
// Architecture:
// - Single bridge (nova0) managed by systemd-networkd
// - VMs connect via TAP devices
// - Containers connect via veth pairs
// - Unified IPAM for both workload types
// - CNI-compatible configuration format
package unified
import (
"net"
"time"
)
// NetworkType defines the type of network connectivity
type NetworkType string
const (
// NetworkBridged connects workload to shared bridge with full L2 connectivity
NetworkBridged NetworkType = "bridged"
// NetworkIsolated creates an isolated network namespace with no connectivity
NetworkIsolated NetworkType = "isolated"
// NetworkHostOnly provides NAT-only connectivity to host network
NetworkHostOnly NetworkType = "host-only"
// NetworkMacvtap provides near-native performance via macvtap
NetworkMacvtap NetworkType = "macvtap"
// NetworkSRIOV provides SR-IOV VF passthrough
NetworkSRIOV NetworkType = "sriov"
// NetworkNone disables networking entirely
NetworkNone NetworkType = "none"
)
// WorkloadType identifies whether this is a VM or container
type WorkloadType string
const (
WorkloadVM WorkloadType = "vm"
WorkloadContainer WorkloadType = "container"
)
// NetworkConfig is the unified configuration for both VMs and containers.
// Compatible with CNI network config format.
type NetworkConfig struct {
// Network name (matches bridge name, e.g., "nova0")
Name string `json:"name"`
// Network type
Type NetworkType `json:"type"`
// Bridge name (for bridged networks)
Bridge string `json:"bridge,omitempty"`
// Subnet in CIDR notation
Subnet string `json:"subnet"`
// Gateway IP address
Gateway string `json:"gateway,omitempty"`
// IPAM configuration
IPAM *IPAMConfig `json:"ipam,omitempty"`
// DNS configuration
DNS *DNSConfig `json:"dns,omitempty"`
// MTU (default: 1500)
MTU int `json:"mtu,omitempty"`
// VLAN ID (optional, for tagged traffic)
VLAN int `json:"vlan,omitempty"`
// EnableHairpin allows traffic to exit and re-enter on same port
EnableHairpin bool `json:"enableHairpin,omitempty"`
// RateLimit in bytes/sec (0 = unlimited)
RateLimit int64 `json:"rateLimit,omitempty"`
}
// IPAMConfig defines IP address management settings
type IPAMConfig struct {
// Type: "static", "dhcp", or "pool"
Type string `json:"type"`
// Subnet (CIDR notation)
Subnet string `json:"subnet"`
// Gateway
Gateway string `json:"gateway,omitempty"`
// Pool start address (for type=pool)
PoolStart string `json:"poolStart,omitempty"`
// Pool end address (for type=pool)
PoolEnd string `json:"poolEnd,omitempty"`
// Static IP address (for type=static)
Address string `json:"address,omitempty"`
// Reservations maps workload ID to reserved IP
Reservations map[string]string `json:"reservations,omitempty"`
}
// DNSConfig defines DNS settings
type DNSConfig struct {
// Nameservers
Nameservers []string `json:"nameservers,omitempty"`
// Search domains
Search []string `json:"search,omitempty"`
// Options
Options []string `json:"options,omitempty"`
}
// Interface represents an attached network interface
type Interface struct {
// Name of the interface (e.g., "tap-abc123", "veth-xyz-h")
Name string `json:"name"`
// MAC address
MAC net.HardwareAddr `json:"mac"`
// IP address (after IPAM allocation)
IP net.IP `json:"ip,omitempty"`
// Subnet mask
Mask net.IPMask `json:"mask,omitempty"`
// Gateway
Gateway net.IP `json:"gateway,omitempty"`
// Bridge this interface is attached to
Bridge string `json:"bridge"`
// Workload ID this interface belongs to
WorkloadID string `json:"workloadId"`
// Workload type (VM or container)
WorkloadType WorkloadType `json:"workloadType"`
// File descriptor (for TAP devices, ready for VMM use)
FD int `json:"-"`
// Container-side interface name (for veth pairs)
PeerName string `json:"peerName,omitempty"`
// Namespace file descriptor (for moving veth to container)
NamespaceRef string `json:"-"`
}
// Lease represents an IP address lease
type Lease struct {
// IP address
IP net.IP `json:"ip"`
// MAC address
MAC net.HardwareAddr `json:"mac"`
// Workload ID
WorkloadID string `json:"workloadId"`
// Lease start time
Start time.Time `json:"start"`
// Lease expiration time
Expires time.Time `json:"expires"`
// Is this a static reservation?
Static bool `json:"static"`
}
// BridgeInfo contains information about a managed bridge
type BridgeInfo struct {
// Bridge name
Name string `json:"name"`
// Bridge MAC address
MAC net.HardwareAddr `json:"mac"`
// IP address on the bridge
IP net.IP `json:"ip,omitempty"`
// Subnet
Subnet *net.IPNet `json:"subnet,omitempty"`
// Attached interfaces
Interfaces []string `json:"interfaces"`
// MTU
MTU int `json:"mtu"`
// Is bridge up?
Up bool `json:"up"`
}

View File

@@ -0,0 +1,25 @@
# Link configuration for Volt TAP devices
# Ensures consistent naming and settings for VM TAPs
#
# Install: cp 90-volt-vmm-tap.link /etc/systemd/network/
[Match]
# Match TAP devices created by Volt
# Pattern: tap-<vm-id> or nova-tap-<vm-id>
OriginalName=tap-* nova-tap-*
Driver=tun
[Link]
# Don't rename these devices (we name them explicitly)
NamePolicy=keep
# Enable multiqueue for better performance
# (requires TUN_MULTI_QUEUE at creation time)
# TransmitQueues=4
# ReceiveQueues=4
# MTU (match bridge MTU)
MTUBytes=1500
# Disable wake-on-lan (not applicable)
WakeOnLan=off

View File

@@ -0,0 +1,17 @@
# Link configuration for Volt/Voltainer veth devices
# Ensures consistent naming and settings for container veths
#
# Install: cp 90-volt-vmm-veth.link /etc/systemd/network/
[Match]
# Match veth host-side devices
# Pattern: veth-<container-id> or nova-veth-<id>
OriginalName=veth-* nova-veth-*
Driver=veth
[Link]
# Don't rename
NamePolicy=keep
# MTU
MTUBytes=1500

View File

@@ -0,0 +1,14 @@
# Template for TAP device attachment to bridge
# Used with systemd template instances: nova-tap@vm123.network
#
# This is auto-generated per-VM, showing the template
[Match]
Name=%i
[Network]
# Attach to the Volt bridge
Bridge=nova0
# No IP on the TAP itself (VM gets IP via DHCP or static)
# The TAP is just a L2 pipe to the bridge

View File

@@ -0,0 +1,14 @@
# Template for veth host-side attachment to bridge
# Used with systemd template instances: nova-veth@container123.network
#
# This is auto-generated per-container, showing the template
[Match]
Name=%i
[Network]
# Attach to the Volt bridge
Bridge=nova0
# No IP on the host-side veth
# Container side gets IP via DHCP or static in its namespace

View File

@@ -0,0 +1,30 @@
# Volt shared bridge device
# Managed by systemd-networkd
# Used by both Volt VMs (TAP) and Voltainer containers (veth)
#
# Install: cp nova0.netdev /etc/systemd/network/
# Apply: systemctl restart systemd-networkd
[NetDev]
Name=nova0
Kind=bridge
Description=Volt unified VM/container bridge
[Bridge]
# Forward delay for fast convergence (microVMs boot fast)
ForwardDelaySec=0
# Enable hairpin mode for container-to-container on same bridge
# This allows traffic to exit and re-enter on the same port
# Useful for service mesh / sidecar patterns
HairpinMode=true
# STP disabled by default (single bridge, no loops)
# Enable if creating multi-bridge topologies
STP=false
# VLAN filtering (optional, for multi-tenant isolation)
VLANFiltering=false
# Multicast snooping for efficient multicast
MulticastSnooping=true

View File

@@ -0,0 +1,62 @@
# Volt bridge network configuration
# Assigns IP to bridge and configures DHCP server
#
# Install: cp nova0.network /etc/systemd/network/
# Apply: systemctl restart systemd-networkd
[Match]
Name=nova0
[Network]
Description=Volt unified network
# Bridge IP address (gateway for VMs/containers)
Address=10.42.0.1/24
# Enable IP forwarding for this interface
IPForward=yes
# Enable IPv6 (optional)
# Address=fd42:nova::1/64
# Enable LLDP for network discovery
LLDP=yes
EmitLLDP=customer-bridge
# Enable built-in DHCP server (systemd-networkd DHCPServer)
# Alternative: use dnsmasq or external DHCP
DHCPServer=yes
# Configure masquerading (NAT) for external access
IPMasquerade=both
[DHCPServer]
# DHCP pool range
PoolOffset=2
PoolSize=252
# Lease time
DefaultLeaseTimeSec=3600
MaxLeaseTimeSec=86400
# DNS servers to advertise
DNS=10.42.0.1
# Use host's DNS if available
# DNS=_server_address
# Router (gateway)
Router=10.42.0.1
# Domain
# EmitDNS=yes
# DNS=10.42.0.1
# NTP server (optional)
# NTP=10.42.0.1
# Timezone (optional)
# Timezone=UTC
[Route]
# Default route through this interface for the subnet
Destination=10.42.0.0/24