Files
ihasvpn/internal/engine/alloc.go
T
jcoffey 6c006e1d4d WireGuard server with an embedded admin console
Go backend that drives kernel WireGuard over netlink (wireguard-go as the
fallback), nftables NAT with MSS clamping, forwarding and buffer sysctls,
SQLite for peers, users, sessions, traffic history and the audit log.

React console: dashboard with live rates and usage history, peer management
with QR codes and .conf downloads, disconnect, session reset, key rotation,
expiry, client-supplied keys, settings, users with admin and viewer roles,
two-factor authentication with recovery codes, audit log.

Docker image on Alpine with compose files for bridged and host networking,
CI and GHCR publish workflows, performance notes.
2026-09-12 19:56:08 -07:00

57 lines
1.7 KiB
Go

package engine
import (
"errors"
"fmt"
"net/netip"
)
// allocate returns the lowest free host address in the subnet, skipping the
// network address, the server's own (first host) and, for IPv4, the
// broadcast address. `used` holds addresses already handed out.
func allocate(subnet netip.Prefix, used map[string]bool) (netip.Addr, error) {
subnet = subnet.Masked()
base := subnet.Addr()
server := base.Next()
var last netip.Addr
if base.Is4() {
// Broadcast: all host bits set.
a := base.As4()
hostBits := 32 - subnet.Bits()
var n uint32 = uint32(a[0])<<24 | uint32(a[1])<<16 | uint32(a[2])<<8 | uint32(a[3])
n |= (1 << hostBits) - 1
last = netip.AddrFrom4([4]byte{byte(n >> 24), byte(n >> 16), byte(n >> 8), byte(n)})
}
// Cap the scan: a /64 is not walked to the end, and nobody has 65k peers.
const maxScan = 65536
addr := server.Next()
for i := 0; i < maxScan && subnet.Contains(addr); i++ {
if base.Is4() && addr == last {
break
}
if !used[addr.String()] {
return addr, nil
}
addr = addr.Next()
}
return netip.Addr{}, errors.New("no free addresses left in " + subnet.String())
}
// checkAddress validates an operator-chosen address for a peer.
func checkAddress(subnet netip.Prefix, s string, used map[string]bool) (netip.Addr, error) {
a, err := netip.ParseAddr(s)
if err != nil {
return netip.Addr{}, fmt.Errorf("%q is not an IP address", s)
}
if !subnet.Contains(a) {
return netip.Addr{}, fmt.Errorf("%s is outside %s", a, subnet.Masked())
}
if a == subnet.Masked().Addr() || a == subnet.Masked().Addr().Next() {
return netip.Addr{}, fmt.Errorf("%s is reserved for the server", a)
}
if used[a.String()] {
return netip.Addr{}, fmt.Errorf("%s is already assigned", a)
}
return a, nil
}