From 5d980514b8ea66d06d2052aa0837be6ddc4850f7 Mon Sep 17 00:00:00 2001 From: GnomeZworc Date: Mon, 30 Mar 2026 23:16:52 +0200 Subject: [PATCH 1/5] f-15: code: add generate dhcp file Signed-off-by: GnomeZworc --- internal/dhcp/generate.go | 47 +++++++++++++++++++++++++++++++++++++++ internal/dhcp/struct.go | 12 ++++++++++ 2 files changed, 59 insertions(+) create mode 100644 internal/dhcp/generate.go create mode 100644 internal/dhcp/struct.go diff --git a/internal/dhcp/generate.go b/internal/dhcp/generate.go new file mode 100644 index 0000000..01bfe20 --- /dev/null +++ b/internal/dhcp/generate.go @@ -0,0 +1,47 @@ +package dhcp + +import ( + "fmt" + "net" + "os" + "path/filepath" + "strings" +) + +func GenerateConfig(c Config) (string, error) { + mask := fmt.Sprintf("%d.%d.%d.%d", c.Network.Mask[0], c.Network.Mask[1], c.Network.Mask[2], c.Network.Mask[3]) + + var sb strings.Builder + fmt.Fprintf(&sb, "no-resolv\n") + fmt.Fprintf(&sb, "dhcp-range=%s,static,%s,12h\n", c.Network.IP.String(), mask) + fmt.Fprintf(&sb, "dhcp-option=3,%s\n", c.Gateway.String()) + fmt.Fprintf(&sb, "dhcp-option=6,1.1.1.1,8.8.8.8\n\n") + + i := 0 + for ip := cloneIP(c.Network.IP); c.Network.Contains(ip); incrementIP(ip) { + fmt.Fprintf(&sb, "dhcp-host=00:22:33:%02X:%02X:%02X,%s\n", + (i>>16)&0xFF, (i>>8)&0xFF, i&0xFF, ip) + i++ + } + + outPath := filepath.Join(c.ConfDir, c.Name+".conf") + if err := os.MkdirAll(c.ConfDir, 0755); err != nil { + return "", err + } + return outPath, os.WriteFile(outPath, []byte(sb.String()), 0644) +} + +func incrementIP(ip net.IP) { + for j := len(ip) - 1; j >= 0; j-- { + ip[j]++ + if ip[j] != 0 { + break + } + } +} + +func cloneIP(ip net.IP) net.IP { + clone := make(net.IP, len(ip)) + copy(clone, ip) + return clone +} diff --git a/internal/dhcp/struct.go b/internal/dhcp/struct.go new file mode 100644 index 0000000..4c69b9c --- /dev/null +++ b/internal/dhcp/struct.go @@ -0,0 +1,12 @@ +package dhcp + +import ( + "net" +) + +type Config struct { + Network *net.IPNet + Gateway net.IP + Name string + ConfDir string +} From a346876cfb8e308d2e3aed526b01f0bb173ea67e Mon Sep 17 00:00:00 2001 From: GnomeZworc Date: Mon, 30 Mar 2026 23:19:34 +0200 Subject: [PATCH 2/5] f-15: code add code for binary Signed-off-by: GnomeZworc --- cmd/dhcp/main.go | 64 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) create mode 100644 cmd/dhcp/main.go diff --git a/cmd/dhcp/main.go b/cmd/dhcp/main.go new file mode 100644 index 0000000..b2ce08d --- /dev/null +++ b/cmd/dhcp/main.go @@ -0,0 +1,64 @@ +package main + +import ( + "flag" + "fmt" + "net" + "os" + + "git.g3e.fr/syonad/two/internal/dhcp" + "git.g3e.fr/syonad/two/pkg/systemd" +) + +func main() { + subnet := flag.String("subnet", "", "Subnet CIDR (e.g. 10.10.10.0/24)") + name := flag.String("name", "", "Config name (e.g. vpc1_br-00002)") + gateway := flag.String("gateway", "", "Gateway IP (e.g. 10.10.10.1)") + confDir := flag.String("confdir", "/etc/dnsmasq.d", "dnsmasq config directory") + flag.Parse() + + if *subnet == "" || *name == "" || *gateway == "" { + flag.Usage() + os.Exit(1) + } + + _, network, err := net.ParseCIDR(*subnet) + if err != nil { + fmt.Fprintf(os.Stderr, "invalid subnet: %v\n", err) + os.Exit(1) + } + + gw := net.ParseIP(*gateway) + if gw == nil { + fmt.Fprintf(os.Stderr, "invalid gateway IP: %q\n", *gateway) + os.Exit(1) + } + + conf := dhcp.Config{ + Network: network, + Gateway: gw, + Name: *name, + ConfDir: *confDir, + } + + confPath, err := dhcp.GenerateConfig(conf) + if err != nil { + fmt.Fprintf(os.Stderr, "error generating config: %v\n", err) + os.Exit(1) + } + fmt.Printf("dnsmasq config written to %s\n", confPath) + + svc, err := systemd.New() + if err != nil { + fmt.Fprintf(os.Stderr, "error connecting to systemd: %v\n", err) + os.Exit(1) + } + defer svc.Close() + + unit := "dnsmasq@" + *name + ".service" + if err := svc.Start(unit); err != nil { + fmt.Fprintf(os.Stderr, "error starting %s: %v\n", unit, err) + os.Exit(1) + } + fmt.Printf("started %s\n", unit) +} From cfbac0034b5ab9e73b975d55282a58d142d6f8f5 Mon Sep 17 00:00:00 2001 From: GnomeZworc Date: Mon, 30 Mar 2026 23:20:24 +0200 Subject: [PATCH 3/5] f-15: ci: add dhcp binary build Signed-off-by: GnomeZworc --- .forgejo/workflows/prerelease.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.forgejo/workflows/prerelease.yml b/.forgejo/workflows/prerelease.yml index f20ca66..8b2fe2d 100644 --- a/.forgejo/workflows/prerelease.yml +++ b/.forgejo/workflows/prerelease.yml @@ -38,6 +38,7 @@ jobs: - metacli - agent - vpc + - dhcp uses: ./.forgejo/workflows/build.yml with: tag: ${{ needs.set-release-target.outputs.release_cible }} @@ -51,4 +52,4 @@ jobs: uses: ./.forgejo/workflows/release.yml with: tag: ${{ needs.set-release-target.outputs.release_cible }} - secrets: inherit \ No newline at end of file + secrets: inherit From ecf5622c2765a11b6bda7abaf2ca1e839629a960 Mon Sep 17 00:00:00 2001 From: GnomeZworc Date: Mon, 30 Mar 2026 23:20:51 +0200 Subject: [PATCH 4/5] f-15: systemd: add systemd unit for dhcp Signed-off-by: GnomeZworc --- systemd/dnsmasq@.service | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 systemd/dnsmasq@.service diff --git a/systemd/dnsmasq@.service b/systemd/dnsmasq@.service new file mode 100644 index 0000000..8d5e732 --- /dev/null +++ b/systemd/dnsmasq@.service @@ -0,0 +1,11 @@ +[Unit] +Description=dnsmasq in netns %i +After=network.target + +[Service] +Type=simple +ExecStart=/usr/local/bin/run-dnsmasq-in-netns.sh %i +ExecStopPost=/bin/rm -f /run/dnsmasq-%i.pid + +[Install] +WantedBy=multi-user.target From 83b1fa678668e60c9dbc874a1d5b9a4972894c14 Mon Sep 17 00:00:00 2001 From: GnomeZworc Date: Mon, 30 Mar 2026 23:21:27 +0200 Subject: [PATCH 5/5] f-15: script: add script for running dhcp in netns Signed-off-by: GnomeZworc --- scripts/run-dnsmasq-in-netns.sh | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 scripts/run-dnsmasq-in-netns.sh diff --git a/scripts/run-dnsmasq-in-netns.sh b/scripts/run-dnsmasq-in-netns.sh new file mode 100644 index 0000000..c0f9253 --- /dev/null +++ b/scripts/run-dnsmasq-in-netns.sh @@ -0,0 +1,21 @@ +#!/bin/bash +set -e + +# Expects one argument: netns_bridge (e.g. vpc-00003_br-00002 or vpc1_br0) +arg="$1" +NETNS="${arg%%_*}" +BRIDGE="${arg#*_}" + +echo "start dnsmasq ${NETNS} ${BRIDGE}" + +exec ip netns exec "${NETNS}" \ + dnsmasq \ + --no-daemon \ + --interface="${BRIDGE}" \ + --bind-interfaces \ + --pid-file="/run/dnsmasq-$arg.pid" \ + --conf-file="/etc/dnsmasq.d/$arg.conf" \ + --no-hosts \ + --no-resolv \ + --log-facility="/var/log/dnsmasq-$arg.log" \ + --no-daemon -p0