Separate repositories

This commit is contained in:
Alex D. 2024-11-06 10:50:02 +00:00
parent 67a3c648ce
commit a7e5c7629a
Signed by: caskd
GPG Key ID: F92BA85F61F4C173
23 changed files with 24 additions and 976 deletions

1
.gitignore vendored
View File

@ -0,0 +1 @@
workstation/

View File

@ -1,7 +0,0 @@
Copyright 2024 Alex Denes
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@ -1,3 +0,0 @@
# s6-netdev
This is a project which can generate s6 service definitions based on a structured definition. It's intended to be used together with nnd but can be adapted to be used with any other distribution based on s6.

View File

@ -1,30 +0,0 @@
package s6netdev
import (
"fmt"
)
func (t *S6SvcTree) netdevCreateBundleAll(ifsvc *S6Svc) *S6Svc {
bundle_name := S6SvcName("bundle.s6netdev")
b := t.S6New(bundle_name, &S6SvcTypes.Bundle)
b.S6Children(ifsvc)
return b
}
func (t *S6SvcTree) netdevCreateBundleStage(iface, stage string) *S6Svc {
bundle_name := S6SvcName(fmt.Sprintf("bundle.interface.%s", iface))
stage_name := S6SvcName(fmt.Sprintf("%s.%s", bundle_name, stage))
b := t.S6New(bundle_name, &S6SvcTypes.Bundle)
t.netdevCreateBundleAll(b)
s := t.S6New(stage_name, &S6SvcTypes.Bundle)
b.S6Children(s)
return s
}
func (t *S6SvcTree) NetdevDependBundleStage(iface, stage string, svcs ...*S6Svc) *S6Svc {
b := t.netdevCreateBundleStage(iface, stage)
b.S6Children(svcs...)
return b
}

View File

@ -1,314 +0,0 @@
package s6netdev
import (
"fmt"
"net"
"net/netip"
"strings"
)
func (t *S6SvcTree) NetdevIfaceCreate(iface Iface, iftype *NetdevIfType) *S6Svc {
l := t.CreateIfaceService(iface.Name)
// Defaults to be overridden
l.Up = strings.Join([]string{
NETDEV_EXECLINE_HEADER,
ExeclineDefine("INTERFACE", iface.Name),
fmt.Sprintf("ip link add $INTERFACE type %s", iface.Type.str),
}, "\n")
l.Down = strings.Join([]string{
NETDEV_EXECLINE_HEADER,
ExeclineDefine("INTERFACE", iface.Name),
"ip link del dev $INTERFACE",
}, "\n")
for _, v := range iftype.deps {
// External dummy services, should be ignored at commit
cs := t.S6New(S6SvcName(v), &S6SvcTypes.Oneshot)
l.S6Children(cs)
}
// Overrides
switch iftype {
case &NetdevIfTypes.Phys:
{
l.Up = strings.Join([]string{
NETDEV_EXECLINE_HEADER,
ExeclineDefine("INTERFACE", iface.Name),
"bcnm-waitif 1 $INTERFACE",
}, "\n")
l.Down = ""
}
case &NetdevIfTypes.Vlan:
{
l.Up = strings.Join([]string{
NETDEV_EXECLINE_HEADER,
ExeclineDefine("INTERFACE", iface.Name),
ExeclineDefine("PARENT", iface.Parent.Name),
ExeclineDefine("VLAN", fmt.Sprintf("%d", iface.VlanId)),
fmt.Sprintf("ip link add link $PARENT name $INTERFACE type %s id $VLAN", iface.Type.str),
}, "\n")
l.S6Children(t.CreateIfaceService(iface.Parent.Name))
}
case &NetdevIfTypes.Vrf:
{
l.Up = strings.Join([]string{
NETDEV_EXECLINE_HEADER,
ExeclineDefine("INTERFACE", iface.Name),
ExeclineDefine("TABLE", fmt.Sprintf("%d", iface.Table)),
fmt.Sprintf("ip link add $INTERFACE type %s table $TABLE", iface.Type.str),
}, "\n")
}
}
return l
}
func (t *S6SvcTree) NetdevIfaceIpSysctl(iface Iface, ipv int, p Property) *S6Svc {
svc_name := S6SvcName(fmt.Sprintf("sysctl.net-ipv%d-conf-%s-%s", ipv, iface.Name, p.Key)).Sanitize()
sysctl_path := fmt.Sprintf("net/ipv%d/conf/%s/%s", ipv, iface.Name, p.Key)
l := t.S6New(svc_name, &S6SvcTypes.Oneshot)
l.S6Children(t.S6New("mount.proc", &S6SvcTypes.Oneshot))
if ipv == 6 {
l.S6Children(t.S6New("module.ipv6", &S6SvcTypes.Oneshot))
}
for _, v := range []struct {
prop *string
val string
}{
{&l.Up, p.Value},
{&l.Down, p.Default},
} {
*v.prop = strings.Join([]string{
NETDEV_EXECLINE_HEADER,
ExeclineXDGConfig(),
ExeclineXDGEnvdirConfig(l.Name),
ExeclineExport("SYSCTL", sysctl_path),
// Set default if not exported
ExeclineImportas("VAL", false, v.val),
ExeclineExport("VAL", "$VAL"),
NNDLibExec("sysctl"),
}, "\n")
}
l.S6Children(t.CreateIfaceService(iface.Name))
t.NetdevDependBundleStage(iface.Name, "configure", l)
return l
}
func (t *S6SvcTree) NetdevIfaceProp(iface Iface, p Property) *S6Svc {
svc_name := S6SvcName(fmt.Sprintf("interface.%s.property.%s", iface.Name, p.Key)).Sanitize()
l := t.S6New(svc_name, &S6SvcTypes.Oneshot)
for _, v := range []struct {
prop *string
val string
}{
{&l.Up, p.Value},
{&l.Down, p.Default},
} {
*v.prop = strings.Join([]string{
NETDEV_EXECLINE_HEADER,
ExeclineXDGConfig(),
ExeclineXDGEnvdirConfig(l.Name),
ExeclineDefine("INTERFACE", iface.Name),
ExeclineDefine("PROP", p.Key),
ExeclineImportas("TYPE", false, iface.Type.str),
ExeclineImportas("VAL", false, v.val),
"ip link set dev $INTERFACE type $TYPE $PROP $VAL",
}, "\n")
}
l.S6Children(t.CreateIfaceService(iface.Name))
t.NetdevDependBundleStage(iface.Name, "configure", l)
return l
}
func (t *S6SvcTree) NetdevConfigureIfaceMAC(iface Iface, addr net.HardwareAddr) *S6Svc {
svc_name := S6SvcName(fmt.Sprintf("interface.%s.ether.addr", iface.Name)).Sanitize()
l := t.S6New(svc_name, &S6SvcTypes.Oneshot)
l.Up = strings.Join([]string{
NETDEV_EXECLINE_HEADER,
ExeclineXDGConfig(),
ExeclineXDGEnvdirConfig(l.Name),
ExeclineDefine("INTERFACE", iface.Name),
ExeclineImportas("ADDRESS", false, addr.String()),
"ip link set $INTERFACE address $ADDRESS",
}, "\n")
l.S6Children(t.CreateIfaceService(iface.Name))
t.NetdevDependBundleStage(iface.Name, "configure", l)
return l
}
func (t *S6SvcTree) NetdevWireguardConfig(iface Iface) *S6Svc {
svc_name := S6SvcName(fmt.Sprintf("interface.%s.wg.config", iface.Name)).Sanitize()
l := t.S6New(svc_name, &S6SvcTypes.Oneshot)
l.Up = strings.Join([]string{
NETDEV_EXECLINE_HEADER,
ExeclineXDGConfig(),
ExeclineXDGEnvdirConfig(l.Name),
ExeclineDefine("INTERFACE", iface.Name),
ExeclineImportas("CONFIG", false, "/etc/wireguard/${INTERFACE}"),
"wg setconf $INTERFACE $CONFIG",
}, "\n")
l.S6Children(t.CreateIfaceService(iface.Name))
t.NetdevDependBundleStage(iface.Name, "configure", l)
return l
}
func (t *S6SvcTree) NetdevIfaceDHCP(iface Iface, ipv int) *S6Svc {
svc_name := S6SvcName(fmt.Sprintf("interface.%s.dhcp", iface.Name)).Sanitize()
l := t.S6New(svc_name, &S6SvcTypes.Longrun)
var daemon = "udhcpc"
if ipv == 6 {
daemon = fmt.Sprintf("%s%d", daemon, ipv)
}
l.Run = strings.Join([]string{
NETDEV_EXECLINE_HEADER,
ExeclineDefine("INTERFACE", iface.Name),
"fdmove -c 2 1",
fmt.Sprintf("%s -i $INTERFACE -f -S", daemon),
}, "\n")
l.ProducerFor = t.S6New("logger.udhcpc", &S6SvcTypes.Longrun)
l.S6Children(t.LinkIfaceService(iface.Name))
t.NetdevDependBundleStage(iface.Name, "ready", l)
return l
}
func (t *S6SvcTree) netdevIfaceAddrTemplate(action, iface string, ipv int, addr netip.Prefix) string {
return strings.Join([]string{
NETDEV_EXECLINE_HEADER,
ExeclineDefine("INTERFACE", iface),
ExeclineDefine("ADDRESS", addr.String()),
ExeclineDefine("FAMILY", fmt.Sprintf("%d", ipv)),
fmt.Sprintf("ip -${FAMILY} address %s $ADDRESS dev $INTERFACE", action),
}, "\n")
}
func (t *S6SvcTree) NetdevIfaceAddr(iface Iface, addr netip.Prefix) *S6Svc {
ipv := NetdevIPAddrVer(addr.Addr())
svc_name := S6SvcName(fmt.Sprintf("interface.%s.ip.addr.%d.%s", iface.Name, ipv, addr)).Sanitize()
l := t.S6New(svc_name, &S6SvcTypes.Oneshot)
l.Up = t.netdevIfaceAddrTemplate("add", iface.Name, ipv, addr)
l.Down = t.netdevIfaceAddrTemplate("del", iface.Name, ipv, addr)
l.S6Children(t.LinkIfaceService(iface.Name))
t.NetdevDependBundleStage(iface.Name, "ready", l)
return l
}
func (t *S6SvcTree) netdevRouteTemplate(iface Iface, route Route) (up, down string, name S6SvcName) {
var (
lines []string
svcname = []string{"interface", iface.Name, "ip.route"}
ipv = route.IPver
header = []string{
NETDEV_EXECLINE_HEADER,
ExeclineXDGConfig(),
}
)
for _, v := range []netip.Addr{route.Net.Addr(), route.Via} {
if ipv != 0 {
break
}
if v.IsValid() {
ipv = NetdevIPAddrVer(v)
}
}
if ipv == 0 {
// Assume IPv6 if cannot derive
ipv = 6
}
svcname = append(svcname, fmt.Sprintf("%d", ipv))
var cmd = []string{
"ip",
fmt.Sprintf("-%d", ipv), // IP version
"route",
"%s",
}
if route.Type != "" {
lines = append(lines, ExeclineDefine("TYPE", route.Type))
cmd = append(cmd, "$TYPE")
svcname = append(svcname, route.Type)
}
var r = "default"
if !route.Default {
r = route.Net.String()
}
lines = append(lines, ExeclineDefine("ROUTE", r))
cmd = append(cmd, "$ROUTE")
svcname = append(svcname, r)
if route.Via.IsValid() {
lines = append(lines, ExeclineImportas("VIA", false, route.Via.String()))
cmd = append(cmd, "via", "$VIA")
}
lines = append(lines, ExeclineDefine("DEV", iface.Name))
cmd = append(cmd, "dev", "$DEV")
if route.Metric != 0 {
lines = append(lines, ExeclineImportas("METRIC", false, fmt.Sprintf("%d", route.Metric)))
cmd = append(cmd, "metric", "$METRIC")
}
if route.Vrf != nil && route.Vrf.Type == &NetdevIfTypes.Vrf {
// Parent is VRF
lines = append(lines, ExeclineImportas("VRF", false, route.Vrf.Name))
cmd = append(cmd, "vrf", "$VRF")
svcname = append(svcname, "vrf", route.Vrf.Name)
}
name = S6SvcName(strings.Join(svcname, "."))
// Merge
header = append(header, ExeclineXDGEnvdirConfig(name))
lines = append(lines, strings.Join(cmd, " "))
full := append(header, lines...)
up = fmt.Sprintf(strings.Join(full, "\n"), "add")
down = fmt.Sprintf(strings.Join(full, "\n"), "del")
return
}
func (t *S6SvcTree) NetdevRoute(iface Iface, route Route) *S6Svc {
up, down, name := t.netdevRouteTemplate(iface, route)
l := t.S6New(name.Sanitize(), &S6SvcTypes.Oneshot)
l.Up = up
l.Down = down
l.S6Children(t.LinkIfaceService(iface.Name))
if route.Vrf != nil {
l.S6Children(t.LinkIfaceService(route.Vrf.Name))
}
t.NetdevDependBundleStage(iface.Name, "ready", l)
return l
}

View File

@ -5,7 +5,7 @@ import (
"log"
"os"
"git.redxen.eu/caskd/s6-netdev"
"git.redxen.eu/nnd/s6-netdev"
)
func main() {

View File

@ -1,41 +0,0 @@
package s6netdev
import (
"fmt"
"strings"
)
const (
NETDEV_EXECLINE_HEADER = "#!/bin/execlineb -P"
)
func ExeclineDefine(param, value string) string {
return fmt.Sprintf("define %s %s", param, value)
}
func ExeclineExport(param, value string) string {
return fmt.Sprintf("export %s %s", param, value)
}
func ExeclineXDGEnvdirConfig(name S6SvcName) string {
return fmt.Sprintf("s6-envdir -I ${XDG_CONFIG_HOME}/env/%s", name.Sanitize())
}
func ExeclineImportas(name string, strict bool, def string) string {
var s = []string{
"importas",
}
if strict {
s = append(s, "-i")
}
if def != "" {
s = append(s, "-D", def)
}
// Always assume we import to same name
s = append(s, name, name)
return strings.Join(s, " ")
}
func ExeclineXDGConfig() string {
return ExeclineImportas("XDG_CONFIG_HOME", true, "")
}

View File

@ -4,7 +4,7 @@ import (
"log"
"os"
"git.redxen.eu/caskd/s6-netdev"
"git.redxen.eu/nnd/s6-netdev"
)
func main() {

4
go.mod
View File

@ -1,3 +1,5 @@
module git.redxen.eu/caskd/s6-netdev
go 1.22.5
go 1.22.8
require git.redxen.eu/nnd/s6-netdev v0.0.0-20241106103743-03b649623f4e

2
go.sum Normal file
View File

@ -0,0 +1,2 @@
git.redxen.eu/nnd/s6-netdev v0.0.0-20241106103743-03b649623f4e h1:t4/qeNtyQNYFcWQMw7akxafvWJT8+8yErqNiu/Y/1/o=
git.redxen.eu/nnd/s6-netdev v0.0.0-20241106103743-03b649623f4e/go.mod h1:3P2jgoez/bos+RdMFoDCs63PlCKFw1POQz/TfE1dq5g=

View File

@ -1,72 +0,0 @@
package s6netdev
import (
"fmt"
"net"
"net/netip"
)
type IfMap map[string]*Iface
func (im IfMap) AddIf(i *Iface) {
im[i.Name] = i
}
func (i *Iface) NetdevIfaceAddAddr(addr string) (err error) {
var (
a netip.Prefix
)
if a, err = netip.ParsePrefix(addr); err != nil {
err = fmt.Errorf("Failed to parse address %s, %w", addr, err)
}
i.Addresses = append(i.Addresses, a)
return
}
func (i *Iface) NetdevIfaceAddRoute(def bool, route, r_type, via string, vrf *Iface, table *RouteTable, metric Metric) (err error) {
var (
r = Route{
Default: def,
Type: r_type,
Vrf: vrf,
Table: table,
Metric: metric,
}
)
if via != "" {
if r.Via, err = netip.ParseAddr(via); err != nil {
err = fmt.Errorf("Failed to parse via %s, %w", via, err)
}
}
if !def {
if r.Net, err = netip.ParsePrefix(route); err != nil {
err = fmt.Errorf("Failed to parse via %s, %w", via, err)
}
}
i.Routes = append(i.Routes, r)
return
}
func (i *Iface) NetdevIfaceMAC(mac string) (err error) {
i.MACAddr, err = net.ParseMAC(mac)
return
}
func (i *Iface) NetdevIfaceEnslave(s ...*Iface) {
i.Slaves = append(i.Slaves, s...)
}
func (i *Iface) NetdevIfaceSysctl(ipv int, key, value, def string) {
l := &i.Sysctls.V4
if ipv == 6 {
l = &i.Sysctls.V6
}
*l = append(*l, Property{Key: key, Value: value, Default: def})
}
func (i *Iface) NetdevIfaceProperty(key, value, def string) {
i.Properties = append(i.Properties, Property{Key: key, Value: value, Default: def})
}

View File

@ -6,7 +6,7 @@ import (
"net/netip"
"os"
"git.redxen.eu/caskd/s6-netdev"
"git.redxen.eu/nnd/s6-netdev"
)
func main() {

View File

@ -7,7 +7,7 @@ import (
"net/netip"
"os"
"git.redxen.eu/caskd/s6-netdev"
"git.redxen.eu/nnd/s6-netdev"
)
func main() {

View File

@ -5,7 +5,7 @@ import (
"log"
"os"
"git.redxen.eu/caskd/s6-netdev"
"git.redxen.eu/nnd/s6-netdev"
)
func main() {

View File

@ -6,7 +6,7 @@ import (
"net/netip"
"os"
"git.redxen.eu/caskd/s6-netdev"
"git.redxen.eu/nnd/s6-netdev"
)
func main() {
@ -59,20 +59,24 @@ func main() {
{ifn: "tristan-travel", ll: "fe80::1/64", route: "2a04:5b81:201f::/48"},
{ifn: "gustav", ll: "fe80::1/64", route: "2a04:5b81:2011::/48"},
{ifn: "caskd-lakewood", ll: "fe80::1/64", route: "2a04:5b81:2060::/48"},
{ifn: "caskd-thetford", ll: "fe80::1/64", route: ""},
} {
ifs.AddIf(&s6netdev.Iface{
iface := s6netdev.Iface{
Name: v.ifn,
Type: &s6netdev.NetdevIfTypes.Wireguard,
Addresses: []netip.Prefix{netip.MustParsePrefix(v.ll)},
Routes: []s6netdev.Route{
{Net: netip.MustParsePrefix(v.route), Vrf: ifs["vrf-wgate"]},
},
Sysctls: s6netdev.Sysctl_IP{
V6: []s6netdev.Property{
{Key: "forwarding", Value: "1", Default: "0"},
},
},
})
}
if v.route != "" {
iface.Routes: []s6netdev.Route{
{Net: netip.MustParsePrefix(v.route), Vrf: ifs["vrf-wgate"]},
},
}
ifs.AddIf(&iface)
}
for _, m := range []struct {

60
misc.go
View File

@ -1,60 +0,0 @@
package s6netdev
import (
"fmt"
"net"
"net/netip"
"strings"
)
var (
dummy_svc_blacklist = map[S6SvcName]interface{}{
"bundle.hw-coldplug": nil,
"module.8021q": nil,
"module.bonding": nil,
"module.bridge": nil,
"module.vrf": nil,
"module.ipv6": nil,
"module.wireguard": nil,
"mount.proc": nil,
"logger.udhcpc": nil,
}
)
func (t *S6SvcTree) NetdevEnslaveInterface(iface, master string) *S6Svc {
svc_name := S6SvcName(fmt.Sprintf("interface.%s.master.%s", iface, master))
l := t.S6New(svc_name, &S6SvcTypes.Oneshot)
l.Up = strings.Join([]string{
NETDEV_EXECLINE_HEADER,
ExeclineDefine("INTERFACE", iface),
ExeclineDefine("MASTER", master),
"ip link set dev $INTERFACE master $MASTER",
}, "\n")
l.Down = strings.Join([]string{
NETDEV_EXECLINE_HEADER,
ExeclineDefine("INTERFACE", iface),
"ip link set dev $INTERFACE nomaster",
}, "\n")
l.S6Children(t.CreateIfaceService(iface), t.CreateIfaceService(master))
t.NetdevDependBundleStage(iface, "configure", l)
return l
}
func NetdevIPAddrVer(IP netip.Addr) int {
if IP.Is6() {
return 6
}
return 4
}
func NetdevIsDummy(s S6SvcName) (r bool) {
_, r = dummy_svc_blacklist[s]
return
}
func NetdevOnlyMAC(x net.HardwareAddr, _ error) net.HardwareAddr {
return x
}

9
nnd.go
View File

@ -1,9 +0,0 @@
package s6netdev
import (
"fmt"
)
func NNDLibExec(name string) string {
return fmt.Sprintf("/usr/libexec/nnd/s6/%s", name)
}

204
s6.go
View File

@ -1,204 +0,0 @@
package s6netdev
import (
"errors"
"fmt"
"io/fs"
"os"
"path/filepath"
"strings"
)
type S6SvcTree struct {
s map[S6SvcName]*S6Svc
rcdir, envdir string
root string
}
type kv_file struct {
name, value string
perm int
}
type S6Svc struct {
Name S6SvcName
Type S6SvcType
Children []*S6Svc // Either dependencies or contents depending on type
ProducerFor, ConsumerFor *S6Svc
NotifFd int
FlagEssential bool
Up, Down, Run string
env map[string]string
}
type S6SvcName string
func (s S6SvcName) Sanitize() S6SvcName {
// Only sanitize slashes for now
return S6SvcName(strings.ReplaceAll(string(s), "/", "_"))
}
type S6SvcType *string
var S6SvcTypes = struct {
Oneshot, Longrun, Bundle string
}{
Oneshot: "oneshot",
Longrun: "longrun",
Bundle: "bundle",
}
func S6NewTree() *S6SvcTree {
t := new(S6SvcTree)
t.s = make(map[S6SvcName]*S6Svc)
t.root = "/etc/s6"
t.rcdir = "rc"
t.envdir = "env"
return t
}
func (t *S6SvcTree) S6New(name S6SvcName, svc_type S6SvcType) *S6Svc {
if name == "" || svc_type == nil {
return nil
}
if e, ok := t.s[name]; ok && e.Type == svc_type {
return e
}
s := new(S6Svc)
s.Name = name
s.Type = svc_type
t.s[name] = s
return s
}
func S6Pipe(src, dst *S6Svc) {
src.ProducerFor = dst
dst.ConsumerFor = src
}
func (s *S6Svc) S6Children(children ...*S6Svc) {
s.Children = append(s.Children, children...)
}
func (s *S6Svc) S6AddEnv(key, value string) {
s.env[key] = value
}
func (s *S6Svc) S6DelEnv(key string) {
delete(s.env, key)
}
func (t *S6SvcTree) S6AddSvc(s *S6Svc) {
t.s[s.Name] = s
}
func (t *S6SvcTree) S6GetSvc(name S6SvcName) *S6Svc {
return t.s[name]
}
func (t *S6SvcTree) S6Services() (r []*S6Svc) {
for _, v := range t.s {
r = append(r, v)
}
return
}
func (t *S6SvcTree) S6CommitService(s *S6Svc) (err error) {
if err = os.Mkdir(t.rcdir, fs.ModeDir|0777); err != nil && !errors.Is(err, os.ErrExist) {
return
}
sdir := filepath.Join(t.rcdir, string(s.Name))
if err = os.Mkdir(sdir, fs.ModeDir|0777); err != nil && !errors.Is(err, os.ErrExist) {
return
}
var kvfiles []kv_file
kvfiles = append(kvfiles, kv_file{"type", *s.Type, 0666})
if s.FlagEssential {
kvfiles = append(kvfiles, kv_file{"flag-essential", "", 0666})
}
if s.ProducerFor != nil {
kvfiles = append(kvfiles, kv_file{"producer-for", string(s.ProducerFor.Name), 0666})
}
if s.ConsumerFor != nil {
kvfiles = append(kvfiles, kv_file{"consumer-for", string(s.ConsumerFor.Name), 0666})
}
if s.NotifFd != 0 {
kvfiles = append(kvfiles, kv_file{"notification-fd", fmt.Sprintf("%d", s.NotifFd), 0666})
}
// Scripts
switch s.Type {
case &S6SvcTypes.Longrun:
{
if s.Run != "" {
kvfiles = append(kvfiles, kv_file{"run", s.Run, 0777})
}
}
case &S6SvcTypes.Oneshot:
{
if s.Up != "" {
kvfiles = append(kvfiles, kv_file{"up", s.Up, 0777})
}
if s.Down != "" {
kvfiles = append(kvfiles, kv_file{"down", s.Down, 0777})
}
}
}
for _, v := range kvfiles {
var f *os.File
if f, err = os.OpenFile(filepath.Join(sdir, v.name), os.O_CREATE|os.O_RDWR, fs.FileMode(v.perm)); err != nil {
return
}
_, err = fmt.Fprintln(f, v.value)
f.Close()
if err != nil {
return
}
}
// Children
var dirname string
switch s.Type {
case &S6SvcTypes.Bundle:
dirname = "contents.d"
default:
dirname = "dependencies.d"
}
cdir := filepath.Join(sdir, dirname)
if err = os.Mkdir(cdir, fs.ModeDir|0777); err != nil && !errors.Is(err, os.ErrExist) {
return
}
for _, v := range s.Children {
var f *os.File
if f, err = os.Create(filepath.Join(cdir, string(v.Name))); err != nil && !errors.Is(err, os.ErrExist) {
return
}
f.Close()
}
if len(s.env) != 0 {
if err = os.Mkdir(t.envdir, fs.ModeDir|0777); err != nil && !errors.Is(err, os.ErrExist) {
return
}
sdir := filepath.Join(t.envdir, string(s.Name))
if err = os.Mkdir(sdir, fs.ModeDir|0777); err != nil && !errors.Is(err, os.ErrExist) {
return
}
for k, v := range s.env {
var f *os.File
if f, err = os.Create(filepath.Join(sdir, string(k))); err != nil && !errors.Is(err, os.ErrExist) {
return
}
_, err = fmt.Fprintln(f, v)
f.Close()
if err != nil {
return
}
}
}
return
}

View File

@ -6,7 +6,7 @@ import (
"net/netip"
"os"
"git.redxen.eu/caskd/s6-netdev"
"git.redxen.eu/nnd/s6-netdev"
)
func main() {

View File

@ -1,147 +0,0 @@
package s6netdev
import (
"fmt"
"strings"
)
func (t *S6SvcTree) CreateIfaceService(iface string) *S6Svc {
return t.S6New(S6SvcName(fmt.Sprintf("interface.%s.create", iface)), &S6SvcTypes.Oneshot)
}
func (t *S6SvcTree) LinkIfaceService(iface string) *S6Svc {
return t.S6New(S6SvcName(fmt.Sprintf("interface.%s.link", iface)), &S6SvcTypes.Oneshot)
}
func (t *S6SvcTree) Services(i Iface) (r []*S6Svc) {
r = append(r, t.ConfigureServices(i)...)
r = append(r, t.ReadyServices(i)...)
return
}
func (t *S6SvcTree) ConfigureServices(i Iface) (r []*S6Svc) {
svc_create := t.CreateIfaceService(i.Name)
r = append(r, svc_create)
// Defaults to be overridden
svc_create.Up = strings.Join([]string{
NETDEV_EXECLINE_HEADER,
ExeclineDefine("INTERFACE", i.Name),
fmt.Sprintf("ip link add $INTERFACE type %s", i.Type.str),
}, "\n")
svc_create.Down = strings.Join([]string{
NETDEV_EXECLINE_HEADER,
ExeclineDefine("INTERFACE", i.Name),
"ip link del dev $INTERFACE",
}, "\n")
for _, v := range i.Type.deps {
// External dummy services, should be ignored at commit
cs := t.S6New(S6SvcName(v), &S6SvcTypes.Oneshot)
svc_create.S6Children(cs)
}
// Overrides
switch i.Type {
case &NetdevIfTypes.Phys:
fallthrough
case &NetdevIfTypes.Loopback:
{
svc_create.Up = strings.Join([]string{
NETDEV_EXECLINE_HEADER,
ExeclineDefine("INTERFACE", i.Name),
"bcnm-waitif 1 $INTERFACE",
}, "\n")
svc_create.Down = ""
}
case &NetdevIfTypes.Vlan:
{
svc_create.Up = strings.Join([]string{
NETDEV_EXECLINE_HEADER,
ExeclineDefine("INTERFACE", i.Name),
ExeclineDefine("PARENT", i.Parent.Name),
ExeclineDefine("VLAN", fmt.Sprintf("%d", i.VlanId)),
fmt.Sprintf("ip link add link $PARENT name $INTERFACE type %s id $VLAN", i.Type.str),
}, "\n")
svc_create.S6Children(t.CreateIfaceService(i.Parent.Name))
}
case &NetdevIfTypes.Vrf:
{
// Default if no table defined
if i.Table == 0 {
i.Table = RouteTable(512)
}
svc_create.Up = strings.Join([]string{
NETDEV_EXECLINE_HEADER,
ExeclineDefine("INTERFACE", i.Name),
ExeclineDefine("TABLE", fmt.Sprintf("%d", i.Table)),
fmt.Sprintf("ip link add $INTERFACE type %s table $TABLE", i.Type.str),
}, "\n")
}
case &NetdevIfTypes.Wireguard:
{
t.NetdevDependBundleStage(i.Name, "configure", t.NetdevWireguardConfig(i))
}
}
for _, v := range i.Slaves {
r = append(r, t.NetdevEnslaveInterface(v.Name, i.Name))
}
for _, v := range i.Properties {
r = append(r, t.NetdevIfaceProp(i, v))
}
if i.DHCP.V4 {
r = append(r, t.NetdevIfaceDHCP(i, 4))
}
if i.DHCP.V6 {
r = append(r, t.NetdevIfaceDHCP(i, 6))
}
for _, v := range i.Sysctls.V4 {
r = append(r, t.NetdevIfaceIpSysctl(i, 4, v))
}
for _, v := range i.Sysctls.V6 {
r = append(r, t.NetdevIfaceIpSysctl(i, 6, v))
}
for _, v := range i.Addresses {
r = append(r, t.NetdevIfaceAddr(i, v))
}
for _, v := range i.Routes {
r = append(r, t.NetdevRoute(i, v))
}
if i.MACAddr != nil {
r = append(r, t.NetdevConfigureIfaceMAC(i, i.MACAddr))
}
bundle_configure := t.NetdevDependBundleStage(i.Name, "configure", svc_create)
r = append(r, bundle_configure)
r = append(r, svc_create.Children...)
return
}
func (t *S6SvcTree) ReadyServices(i Iface) (r []*S6Svc) {
svc_link := t.S6New(S6SvcName(fmt.Sprintf("interface.%s.link", i.Name)), &S6SvcTypes.Oneshot)
r = append(r, svc_link)
svc_link.Up = strings.Join([]string{
NETDEV_EXECLINE_HEADER,
ExeclineDefine("INTERFACE", i.Name),
"ip link set dev $INTERFACE up",
}, "\n")
svc_link.Down = strings.Join([]string{
NETDEV_EXECLINE_HEADER,
ExeclineDefine("INTERFACE", i.Name),
"ip link set dev $INTERFACE down",
}, "\n")
svc_link.S6Children(t.netdevCreateBundleStage(i.Name, "configure"))
bundle_ready := t.NetdevDependBundleStage(i.Name, "ready", svc_link)
r = append(r, bundle_ready)
return
}

View File

@ -6,7 +6,7 @@ import (
"net/netip"
"os"
"git.redxen.eu/caskd/s6-netdev"
"git.redxen.eu/nnd/s6-netdev"
)
func main() {

View File

@ -7,7 +7,7 @@ import (
"net/netip"
"os"
"git.redxen.eu/caskd/s6-netdev"
"git.redxen.eu/nnd/s6-netdev"
)
func main() {

View File

@ -1,74 +0,0 @@
package s6netdev
import (
"net"
"net/netip"
)
type (
RouteTable uint16
Metric uint32
VLAN uint16
)
type Property struct {
Key, Value, Default string
}
type Sysctl_IP struct {
V4, V6 []Property
}
type DHCP_IP struct {
V4, V6 bool // Should we start dhcp on this interface?
}
type Iface struct {
Name string // Interface name
Type *NetdevIfType // Type of interface
Slaves []*Iface // Slaves for VRFs, Bridges and Bonds etc...
VlanId VLAN // VLAN id for VLAN interfaces
Parent *Iface // Parent interface for VLAN interfaces
Table RouteTable // Routing table, for VRF
MACAddr net.HardwareAddr // MAC address of interface (only valid for physical, bridges and VLAN)
Addresses []netip.Prefix // Addresses to be assigned to interface
Routes []Route // Routes to be assigned to interface
DHCP DHCP_IP // Should we start dhcp on this interface?
Properties []Property // List of properties of the interface, valid for many
Sysctls Sysctl_IP // Sysctls associated with this interface
}
type Route struct {
Default bool
// VRF would be a field but it can be derived from parent
Type string // unicast default, can be others
IPver int // if 0, will guess based on Net
Net netip.Prefix
Via netip.Addr
Vrf *Iface
Table *RouteTable
Metric Metric // Should be explicitly initialised to 1024
}
type NetdevIfType struct {
str string
deps []S6SvcName
}
var NetdevIfTypes = struct {
Phys, Loopback, Bridge, Vlan, Wireguard, Bond, Vrf NetdevIfType
}{
Phys: NetdevIfType{"", []S6SvcName{"bundle.hw-coldplug"}},
Loopback: NetdevIfType{"loopback", []S6SvcName{}},
Bridge: NetdevIfType{"bridge", []S6SvcName{"module.bridge"}},
Vlan: NetdevIfType{"vlan", []S6SvcName{"module.8021q"}},
Wireguard: NetdevIfType{"wireguard", []S6SvcName{"module.wireguard"}},
Bond: NetdevIfType{"bond", []S6SvcName{"module.bonding"}},
Vrf: NetdevIfType{"vrf", []S6SvcName{"module.vrf"}},
}