Switch to golang version

This commit is contained in:
Alex D. 2024-09-04 12:22:10 +00:00
parent f4043a1a9b
commit 0bcad8a5c6
Signed by: caskd
GPG Key ID: F92BA85F61F4C173
24 changed files with 868 additions and 1239 deletions

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
hosts/

7
LICENSE.md Normal file
View File

@ -0,0 +1,7 @@
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,4 +0,0 @@
# KNOWN BUGS
- Currently the static address adds don't check if interface is a wireguard interface
- wg tool doesn't lock the interface when it loads the config and the static address gets swallowed away without any error message

3
README.md Normal file
View File

@ -0,0 +1,3 @@
# 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.

30
bundle.go Normal file
View File

@ -0,0 +1,30 @@
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
}

297
configuration.go Normal file
View File

@ -0,0 +1,297 @@
package s6netdev
import (
"fmt"
"net"
"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) ConfigureIfaceAddr(iface Iface, addr string) *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),
"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.Up = 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 net.IP) 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 net.IP) *S6Svc {
ipv := NetdevIPAddrVer(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, "route"}
cmd = []string{
"ip",
fmt.Sprintf("-%d", NetdevIPAddrVer(route.Net.IP)), // IP version
"route",
"%s",
}
header = []string{
NETDEV_EXECLINE_HEADER,
ExeclineXDGConfig(),
}
)
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 != nil {
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")
}
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
}

41
execline.go Normal file
View File

@ -0,0 +1,41 @@
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

@ -1,25 +0,0 @@
#!/bin/sh
. "$(dirname -- "$0")/netdev.sh"
# Handle loopback specially
# - addresses are assigned automatically when brought up
# - interface is created automatically by the kernel
IFACE="lo"
new_if_phys "$IFACE"
#if_ip_addr "$IFACE" '127.0.0.1/8'
#if_ip_addr "$IFACE" '::1/128'
BRIDGE="br0"
new_if_bridge "$BRIDGE"
if_bridge_property 'mcast_router' "$BRIDGE"
if_bridge_property 'mcast_snooping' "$BRIDGE" '0' ''
if_bridge_property 'mcast_querier' "$BRIDGE"
if_bridge_property 'mcast_mld_version' "$BRIDGE" '2' ''
if_dhcp "$BRIDGE"
if_dhcpv6 "$BRIDGE"
IFACE="eth0"
if_slave "$BRIDGE" "$IFACE"
new_if_phys "$IFACE"

3
go.mod Normal file
View File

@ -0,0 +1,3 @@
module git.redxen.eu/caskd/s6-netdev
go 1.22.5

View File

@ -1,27 +0,0 @@
#!/bin/sh
. "$(dirname -- "$0")/../netdev.sh"
BRIDGE="br-main"
new_if_bridge "$BRIDGE"
if_bridge_property 'mcast_router' "$BRIDGE"
if_bridge_property 'mcast_snooping' "$BRIDGE" '0' ''
if_bridge_property 'mcast_querier' "$BRIDGE"
if_bridge_property 'mcast_mld_version' "$BRIDGE" '2' ''
IFACE="enp1s0"
if_slave "$BRIDGE" "$IFACE"
new_if_phys "$IFACE"
BRIDGE="br-home"
new_if_bridge "$BRIDGE"
if_bridge_property 'mcast_router' "$BRIDGE"
if_bridge_property 'mcast_snooping' "$BRIDGE" '0' ''
if_bridge_property 'mcast_querier' "$BRIDGE"
if_bridge_property 'mcast_mld_version' "$BRIDGE" '2' ''
if_dhcp "$BRIDGE"
if_accept_ra "$BRIDGE" '0' # Disable IPv6 on home net, only ipv4
IFACE="enp11s0"
if_slave "$BRIDGE" "$IFACE"
new_if_phys "$IFACE"

View File

@ -1,42 +0,0 @@
#!/bin/sh
. "$(dirname -- "$0")/../netdev.sh"
IFACE="home-66"
new_if_vlan "$IFACE"
VRF="vrf-home"
new_if_vrf "$VRF" 20
if_route_vrf_default_unreach "$VRF"
BRIDGE="home"
if_slave "$VRF" "$BRIDGE"
new_if_bridge "$BRIDGE"
if_bridge_property 'priority' "$BRIDGE" '16384' '32768'
if_bridge_property 'stp_state' "$BRIDGE"
if_bridge_property 'mcast_router' "$BRIDGE"
if_bridge_property 'mcast_snooping' "$BRIDGE" '0' '' # TODO: Remove such entries when bridges play well with multicasting
if_bridge_property 'mcast_querier' "$BRIDGE"
if_bridge_property 'mcast_mld_version' "$BRIDGE" '2' ''
if_dhcp "$BRIDGE"
IFACE="enp2s0f0"
if_slave "$BRIDGE" "$IFACE"
new_if_phys "$IFACE"
IFACE="enp8s0f0"
if_slave "$BRIDGE" "$IFACE"
new_if_phys "$IFACE"
IFACE="enp4s0f0"
if_slave "$BRIDGE" "$IFACE"
new_if_phys "$IFACE"
IFACE="enp4s0f1"
if_slave "$BRIDGE" "$IFACE"
new_if_phys "$IFACE"
# Wireguard
IFACE="wg-tunnel"
new_if_wg "$IFACE"
if_ip_addr "$IFACE" "172.22.12.7/32"
if_ip_addr "$IFACE" "fd42:42:42::2:7/128"
if_route_addr "$IFACE" "172.22.12.0/24"
if_route_addr "$IFACE" "fd42:42:42::2:0/120"

View File

@ -1,151 +0,0 @@
#!/bin/sh
. "$(dirname -- "$0")/../netdev.sh"
# VRFs
VRF="vrf-dn42"
new_if_vrf "$VRF" 20
if_route_vrf_default_unreach "$VRF"
BRIDGE="br-dn42"
if_slave "$VRF" "$BRIDGE"
new_if_bridge "$BRIDGE"
if_bridge_property 'stp_state' "$BRIDGE" '0'
if_bridge_property 'mcast_router' "$BRIDGE"
if_bridge_property 'mcast_snooping' "$BRIDGE" '0' '' # TODO: Remove such entries when bridges play well with multicasting
if_bridge_property 'mcast_querier' "$BRIDGE"
if_bridge_property 'mcast_mld_version' "$BRIDGE" '2' ''
IFACE="home-42"
if_linkdepend "$IFACE" "${IFACE%-*}"
if_slave "$BRIDGE" "$IFACE"
new_if_vlan "$IFACE"
IFACE="enp2s0"
if_slave "$VRF" "$IFACE"
new_if_phys "$IFACE"
IFACE="famfo"
if_slave "$VRF" "$IFACE"
new_if_wg "$IFACE"
if_ip_addr "$IFACE" "fe80::1422:1/64"
IFACE="mark22k"
if_slave "$VRF" "$IFACE"
new_if_wg "$IFACE"
if_ip_addr "$IFACE" "fe80::4546/64"
IFACE="highdef"
if_slave "$VRF" "$IFACE"
new_if_wg "$IFACE"
if_ip_addr "$IFACE" "fe80::2/64"
IFACE="kioubit"
if_slave "$VRF" "$IFACE"
new_if_wg "$IFACE"
if_ip_addr "$IFACE" "fe80::2/64"
IFACE="lare"
if_slave "$VRF" "$IFACE"
new_if_wg "$IFACE"
if_ip_addr "$IFACE" "fe80::2/64"
IFACE="haktron"
if_slave "$VRF" "$IFACE"
new_if_wg "$IFACE"
if_ip_addr "$IFACE" "fe80::2/64"
VRF="vrf-v6"
new_if_vrf "$VRF" 10
if_route_vrf_sink_unreach "$VRF" "2a04:5b81:2060::/44"
IFACE="intersix"
#if_route_vrf_default_interface "$VRF" "$IFACE"
if_slave "$VRF" "$IFACE"
new_if_wg "$IFACE"
if_ip_addr "$IFACE" "fe80::2/64"
if_ip_addr "$IFACE" "2a04:5b80:ffff:ff0b::2/64"
IFACE="vultrbgp"
if_route_vrf_default_interface "$VRF" "$IFACE"
if_slave "$VRF" "$IFACE"
new_if_wg "$IFACE"
if_ip_addr "$IFACE" "fe80::2/64"
BRIDGE="b00b"
if_slave "$VRF" "$BRIDGE"
new_if_bridge "$BRIDGE"
if_bridge_property 'stp_state' "$BRIDGE" '0'
if_bridge_property 'mcast_router' "$BRIDGE"
if_bridge_property 'mcast_snooping' "$BRIDGE" '0' ''
if_bridge_property 'mcast_querier' "$BRIDGE"
if_bridge_property 'mcast_stats_enabled' "$BRIDGE"
if_bridge_property 'mcast_mld_version' "$BRIDGE" '2' ''
if_ether_address "$BRIDGE" '02:00:00:00:b0:0b'
if_ip_addr "$BRIDGE" "2a04:5b81:2060:b00b::1/64"
IFACE="enp16s0"
if_slave "$BRIDGE" "$IFACE"
new_if_phys "$IFACE"
IFACE="home-66"
if_linkdepend "$IFACE" "${IFACE%-*}"
if_slave "$BRIDGE" "$IFACE"
new_if_vlan "$IFACE"
BRIDGE="f33d"
if_slave "$VRF" "$BRIDGE"
new_if_bridge "$BRIDGE"
if_bridge_property 'stp_state' "$BRIDGE" '0'
if_bridge_property 'mcast_router' "$BRIDGE"
if_bridge_property 'mcast_snooping' "$BRIDGE" '0' ''
if_bridge_property 'mcast_querier' "$BRIDGE"
if_bridge_property 'mcast_mld_version' "$BRIDGE" '2' ''
if_ether_address "$BRIDGE" '02:00:00:00:f3:3d'
if_ip_addr "$BRIDGE" "2a04:5b81:2060:f33d::1/64"
IFACE="enp17s0"
if_slave "$BRIDGE" "$IFACE"
new_if_phys "$IFACE"
IFACE="home-100"
if_linkdepend "$IFACE" "${IFACE%-*}"
if_slave "$BRIDGE" "$IFACE"
new_if_vlan "$IFACE"
BRIDGE="d00d"
if_slave "$VRF" "$BRIDGE"
new_if_bridge "$BRIDGE"
if_bridge_property 'stp_state' "$BRIDGE" '0'
if_bridge_property 'mcast_router' "$BRIDGE"
if_bridge_property 'mcast_snooping' "$BRIDGE" '0' ''
if_bridge_property 'mcast_querier' "$BRIDGE"
if_bridge_property 'mcast_mld_version' "$BRIDGE" '2' ''
if_ether_address "$BRIDGE" '02:00:00:00:d0:0d'
if_ip_addr "$BRIDGE" "2a04:5b81:2060:d00d::1/64"
IFACE="enp18s0"
if_slave "$BRIDGE" "$IFACE"
new_if_phys "$IFACE"
IFACE="home-101"
if_linkdepend "$IFACE" "${IFACE%-*}"
if_slave "$BRIDGE" "$IFACE"
new_if_vlan "$IFACE"
# NO-vrf
BRIDGE="home"
new_if_bridge "$BRIDGE"
if_bridge_property 'stp_state' "$BRIDGE"
if_bridge_property 'mcast_router' "$BRIDGE"
if_bridge_property 'mcast_snooping' "$BRIDGE" '0' '' # TODO: Remove such entries when bridges play well with multicasting
if_bridge_property 'mcast_querier' "$BRIDGE"
if_bridge_property 'mcast_mld_version' "$BRIDGE" '2' ''
if_dhcp "$BRIDGE"
if_forward "$BRIDGE"
IFACE="enp13s0"
if_slave "$BRIDGE" "$IFACE"
new_if_phys "$IFACE"

View File

@ -1,32 +0,0 @@
#!/bin/sh
. "$(dirname -- "$0")/../netdev.sh"
VRF="vrf-v6"
new_if_vrf "$VRF" 20
if_route_vrf_default_unreach "$VRF"
BRIDGE="br-v6"
if_slave "$VRF" "$BRIDGE"
new_if_bridge "$BRIDGE"
if_bridge_property 'mcast_router' "$BRIDGE"
if_bridge_property 'mcast_snooping' "$BRIDGE" '0' ''
if_bridge_property 'mcast_querier' "$BRIDGE"
if_bridge_property 'mcast_mld_version' "$BRIDGE" '2' ''
if_dhcp "$BRIDGE"
IFACE="enp1s0"
if_slave "$BRIDGE" "$IFACE"
new_if_phys "$IFACE"
BRIDGE="br-home"
new_if_bridge "$BRIDGE"
if_bridge_property 'mcast_router' "$BRIDGE"
if_bridge_property 'mcast_snooping' "$BRIDGE" '0' ''
if_bridge_property 'mcast_querier' "$BRIDGE"
if_bridge_property 'mcast_mld_version' "$BRIDGE" '2' ''
if_dhcp "$BRIDGE"
IFACE="enp9s0"
if_slave "$BRIDGE" "$IFACE"
new_if_phys "$IFACE"

View File

@ -1,65 +0,0 @@
#!/bin/sh
. "$(dirname -- "$0")/../netdev.sh"
# VRFs
VRF="vrf-wgate"
new_if_vrf "$VRF" 20
if_route_vrf_sink_unreach "$VRF" "2a04:5b81:2060::/44"
if_route_vrf_sink_unreach "$VRF" "2a04:5b81:2010::/44"
if_route_vrf_default_unreach "$VRF"
# Bridges
IFACE="br-uplink"
if_slave "$VRF" "$IFACE"
new_if_bridge "$IFACE"
if_bridge_property 'stp_state' "$IFACE"
if_bridge_property 'forward_delay' "$IFACE" '400' # 4 seconds, 8 seconds total (listen>learn)
if_bridge_property 'hello_time' "$IFACE" '100' # every 1 second
if_bridge_property 'mcast_router' "$IFACE"
if_bridge_property 'mcast_snooping' "$IFACE" '0' '' # TODO: Remove such entries when bridges play well with multicasting
if_bridge_property 'mcast_querier' "$IFACE"
if_bridge_property 'mcast_mld_version' "$IFACE" '2' ''
if_forward "$IFACE"
if_accept_ra "$IFACE"
if_dhcp "$IFACE"
if_slave "$IFACE" "enp1s0"
new_if_phys "enp1s0"
# Wireguard
IFACE="tristan"
if_slave "$VRF" "$IFACE"
new_if_wg "$IFACE"
if_forward "$IFACE"
if_ip_addr "$IFACE" "fe80::1/64"
if_route_vrf_addr "$VRF" "$IFACE" "2a04:5b81:2010::/48"
IFACE="tristan-travel"
if_slave "$VRF" "$IFACE"
new_if_wg "$IFACE"
if_forward "$IFACE"
if_ip_addr "$IFACE" "fe80::1/64"
if_route_vrf_addr "$VRF" "$IFACE" "2a04:5b81:201f::/48"
IFACE="gustav"
if_slave "$VRF" "$IFACE"
new_if_wg "$IFACE"
if_forward "$IFACE"
if_ip_addr "$IFACE" "fe80::1/64"
if_route_vrf_addr "$VRF" "$IFACE" "2a04:5b81:2011::/48"
IFACE="caskd-lakewood"
if_slave "$VRF" "$IFACE"
new_if_wg "$IFACE"
if_forward "$IFACE"
if_ip_addr "$IFACE" "fe80::1/64"
if_route_vrf_addr "$VRF" "$IFACE" "2a04:5b81:2060::/48"
IFACE="caskd-thetford"
if_slave "$VRF" "$IFACE"
new_if_wg "$IFACE"
if_forward "$IFACE"
if_ip_addr "$IFACE" "fe80::1/64"
if_route_vrf_addr "$VRF" "$IFACE" "2a04:5b81:2060::/48"

View File

@ -1,22 +0,0 @@
#!/bin/sh
. "$(dirname -- "$0")/../netdev.sh"
BRIDGE="br-v6"
new_if_bridge "$BRIDGE"
if_bridge_property 'mcast_router' "$BRIDGE"
if_bridge_property 'mcast_snooping' "$BRIDGE" '0' ''
if_bridge_property 'mcast_querier' "$BRIDGE"
if_bridge_property 'mcast_mld_version' "$BRIDGE" '2' ''
if_dhcp "$BRIDGE"
IFACE="enp1s0"
if_slave "$BRIDGE" "$IFACE"
new_if_phys "$IFACE"
IFACE="wg-tunnel"
new_if_wg "$IFACE"
if_ip_addr "$IFACE" "172.22.12.8/32"
if_ip_addr "$IFACE" "fd42:42:42::2:8/128"
if_route_addr "$IFACE" "172.22.12.0/24"
if_route_addr "$IFACE" "fd42:42:42::2:0/120"

View File

@ -1,39 +0,0 @@
#!/bin/sh
. "$(dirname -- "$0")/../netdev.sh"
IFACE="home-66"
new_if_vlan "$IFACE"
VRF="vrf-home"
new_if_vrf "$VRF" 20
if_route_vrf_default_unreach "$VRF"
BRIDGE="home"
if_slave "$VRF" "$BRIDGE"
new_if_bridge "$BRIDGE"
if_bridge_property 'priority' "$BRIDGE" '16384' '32768'
if_bridge_property 'stp_state' "$BRIDGE"
if_bridge_property 'mcast_router' "$BRIDGE"
if_bridge_property 'mcast_snooping' "$BRIDGE" '0' '' # TODO: Remove such entries when bridges play well with multicasting
if_bridge_property 'mcast_querier' "$BRIDGE"
if_bridge_property 'mcast_mld_version' "$BRIDGE" '2' ''
if_dhcp "$BRIDGE"
IFACE="eno1"
if_slave "$BRIDGE" "$IFACE"
new_if_phys "$IFACE"
IFACE="enp129s0f0"
if_slave "$BRIDGE" "$IFACE"
new_if_phys "$IFACE"
IFACE="enp129s0f1"
if_slave "$BRIDGE" "$IFACE"
new_if_phys "$IFACE"
# Wireguard
IFACE="wg-tunnel"
new_if_wg "$IFACE"
if_ip_addr "$IFACE" "172.22.12.5/32"
if_ip_addr "$IFACE" "fd42:42:42::2:5/128"
if_route_addr "$IFACE" "172.22.12.0/24"
if_route_addr "$IFACE" "fd42:42:42::2:0/120"

View File

@ -1,113 +0,0 @@
#!/bin/sh
. "$(dirname -- "$0")/../netdev.sh"
VRF="vrf-dn42"
new_if_vrf "$VRF" 20
if_route_vrf_default_unreach "$VRF"
BRIDGE="br-dn42"
if_slave "$VRF" "$BRIDGE"
new_if_bridge "$BRIDGE"
if_bridge_property 'stp_state' "$BRIDGE" '0'
if_bridge_property 'mcast_router' "$BRIDGE"
if_bridge_property 'mcast_snooping' "$BRIDGE" '0' '' # TODO: Remove such entries when bridges play well with multicasting
if_bridge_property 'mcast_querier' "$BRIDGE"
if_bridge_property 'mcast_mld_version' "$BRIDGE" '2' ''
IFACE="enp15s0"
if_slave "$BRIDGE" "$IFACE"
new_if_phys "$IFACE"
IFACE="phys-42"
if_linkdepend "$IFACE" "${IFACE%-*}"
if_slave "$BRIDGE" "$IFACE"
new_if_vlan "$IFACE"
VRF="vrf-v6"
new_if_vrf "$VRF" 10
#if_route_vrf_sink_unreach "$VRF" "2a04:5b81:2060::/44"
IFACE="vultrbgp"
#if_route_vrf_default_interface "$VRF" "$IFACE"
if_slave "$VRF" "$IFACE"
new_if_wg "$IFACE"
if_ip_addr "$IFACE" "fe80::2/64"
BRIDGE="b00b"
if_slave "$VRF" "$BRIDGE"
if_sysctl "$BRIDGE" 'forwarding' '0'
if_sysctl "$BRIDGE" 'autoconf' '0'
new_if_bridge "$BRIDGE"
if_bridge_property 'stp_state' "$BRIDGE" '0'
if_bridge_property 'mcast_router' "$BRIDGE"
if_bridge_property 'mcast_snooping' "$BRIDGE" '0' ''
if_bridge_property 'mcast_querier' "$BRIDGE"
if_bridge_property 'mcast_stats_enabled' "$BRIDGE"
if_bridge_property 'mcast_mld_version' "$BRIDGE" '2' ''
if_ether_address "$BRIDGE" '02:00:00:01:b0:0b'
if_ip_addr "$BRIDGE" "2a04:5b81:2060:b00b::2/64"
IFACE="enp9s0"
if_slave "$BRIDGE" "$IFACE"
new_if_phys "$IFACE"
IFACE="phys-66"
if_linkdepend "$IFACE" "${IFACE%-*}"
if_slave "$BRIDGE" "$IFACE"
new_if_vlan "$IFACE"
BRIDGE="f33d"
if_slave "$VRF" "$BRIDGE"
new_if_bridge "$BRIDGE"
if_bridge_property 'stp_state' "$BRIDGE" '0'
if_bridge_property 'mcast_router' "$BRIDGE"
if_bridge_property 'mcast_snooping' "$BRIDGE" '0' '' # TODO: Remove such entries when bridges play well with multicasting
if_bridge_property 'mcast_querier' "$BRIDGE"
if_bridge_property 'mcast_mld_version' "$BRIDGE" '2' ''
if_ether_address "$BRIDGE" '02:00:00:01:f3:3d'
if_ip_addr "$BRIDGE" "2a04:5b81:2060:f33d::2/64"
IFACE="enp14s0"
if_slave "$BRIDGE" "$IFACE"
new_if_phys "$IFACE"
IFACE="phys-100"
if_linkdepend "$IFACE" "${IFACE%-*}"
if_slave "$BRIDGE" "$IFACE"
new_if_vlan "$IFACE"
BRIDGE="d00d"
if_slave "$VRF" "$BRIDGE"
new_if_bridge "$BRIDGE"
if_bridge_property 'stp_state' "$BRIDGE" '0'
if_bridge_property 'mcast_router' "$BRIDGE"
if_bridge_property 'mcast_snooping' "$BRIDGE" '0' '' # TODO: Remove such entries when bridges play well with multicasting
if_bridge_property 'mcast_querier' "$BRIDGE"
if_bridge_property 'mcast_mld_version' "$BRIDGE" '2' ''
if_ether_address "$BRIDGE" '02:00:00:01:d0:0d'
if_ip_addr "$BRIDGE" "2a04:5b81:2060:d00d::2/64"
IFACE="enp13s0"
if_slave "$BRIDGE" "$IFACE"
new_if_phys "$IFACE"
IFACE="phys-101"
if_linkdepend "$IFACE" "${IFACE%-*}"
if_slave "$BRIDGE" "$IFACE"
new_if_vlan "$IFACE"
BRIDGE="phys"
new_if_bridge "$BRIDGE"
if_bridge_property 'stp_state' "$BRIDGE"
if_ether_address "$BRIDGE" '52:54:00:81:cb:62'
if_bridge_property 'mcast_router' "$BRIDGE"
if_bridge_property 'mcast_snooping' "$BRIDGE" '0' '' # TODO: Remove such entries when bridges play well with multicasting
if_bridge_property 'mcast_querier' "$BRIDGE"
if_bridge_property 'mcast_mld_version' "$BRIDGE" '2' ''
if_dhcp "$BRIDGE"
if_forward "$BRIDGE"
IFACE="enp12s0"
if_slave "$BRIDGE" "$IFACE"
new_if_phys "$IFACE"

137
interfaces.go Normal file
View File

@ -0,0 +1,137 @@
package s6netdev
import (
"fmt"
"strings"
)
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(500)
}
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:
{
wg_cfg := t.NetdevWireguardConfig(i)
svc_create.S6Children(wg_cfg)
r = append(r, wg_cfg)
}
}
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))
}
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.CreateIfaceService(i.Name))
bundle_ready := t.NetdevDependBundleStage(i.Name, "ready", svc_link)
r = append(r, bundle_ready)
return
}

55
netdev.go Normal file
View File

@ -0,0 +1,55 @@
package s6netdev
import (
"fmt"
"net"
"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 net.IP) int {
if IP.To4() == nil {
return 6
}
return 4
}
func NetdevIsDummy(s S6SvcName) (r bool) {
_, r = dummy_svc_blacklist[s]
return
}

719
netdev.sh
View File

@ -1,719 +0,0 @@
#!/bin/sh
#
# Generate s6 network definitions
set -ex
header_eb() {
echo '#!/bin/execlineb -P'
}
header_if() {
local if="${1:?missing if}"
echo "define INTERFACE $if"
}
header_addr() {
local addr="${1:?missing addr}"
echo "define ADDR $addr"
}
header_fam() {
local fam="${1:?missing family}"
echo "define FAMILY $fam"
}
header_vlan() {
local vlan="${1:?missing vlan}"
local parent="${2:?missing parent}"
echo "define VLAN $vlan"
echo "define PARENT $parent"
}
header_vrf() {
local vrf="${1:?missing vrf}"
echo "define VRF $vrf"
}
header_prop() {
local prop="${1:?missing prop}"
echo "define PROP $prop"
}
addrfam() {
# Use ipv6 if cannot autodetect of if no semicolons are present
local addr="$1"
local fam='6'
if which grep >/dev/null 2>&1 && echo "$addr" | grep -v ':' >/dev/null 2>&1; then
fam='4'
fi
echo "$fam"
}
linkdel() {
local if="${1:?missing if}"
header_eb
header_if "$if"
echo 'ip link del $INTERFACE'
}
depends() {
echo "$@"
local svcname="${1:?missing sname}"
shift 1
local sname # God i fucking hate global contexts by default
for sname in $@; do
install -Dm644 /dev/null rc/"$svcname"/dependencies.d/"$sname"
done
}
contains() {
local svcname="${1:?missing sname}"
shift 1
local sname # God i fucking hate global contexts by default
for sname in $@; do
install -Dm644 /dev/null rc/"$svcname"/contents.d/"$sname"
done
}
#
# Bundle management
#
bundle_deps() {
local if="${1:?missing if}"
local svcname="bundle.interface.$if"
[ -r "rc/$svcname/type" ] || install -Dm644 <(echo bundle) rc/"$svcname"/type
shift 1
contains "$svcname" $@
}
# Currently used stages
# > configure
# - create
# - enslave
# - configure (anything that must be configured before interface is up)
# > ready
# - link
# - configure (anything that may be configured after interface is up)
bundle_stage_create() {
local if="${1:?missing if}"
local stage="${2:?missing stage}"
local svcname="bundle.interface.$if.$stage"
[ -r "rc/$svcname/type" ] || install -Dm644 <(echo bundle) rc/"$svcname"/type
# Add to parent bundle
bundle_deps "$if" "$svcname"
shift 2
contains "$svcname" $@
}
bundle_stage_depend_on() {
local if="${1:?missing if}"
local stage="${2:?missing stage}"
local svcname="bundle.interface.$if.$stage"
shift 2
local sname
for sname in $@; do
depends "$sname" "$svcname"
done
}
bundle_stage_step_configure() {
local if="$1"
local stage="configure"
shift 1
bundle_stage_create "$if" "$stage" $@
}
bundle_stage_step_ready() {
local if="$1"
local stage="ready"
shift 1
bundle_stage_create "$if" "$stage" $@
}
#
# Interface creation
#
new_link() {
local if="${1:?missing if}"
shift 1
local sname="interface.$if.link"
bundle_stage_step_ready "$if" "$sname"
install -Dm644 <(
header_eb
header_if "$if"
echo 'ip link set dev $INTERFACE up'
) rc/"$sname"/up
install -Dm644 <(
header_eb
header_if "$if"
echo 'ip link set dev $INTERFACE down'
) rc/"$sname"/down
install -Dm644 <(echo oneshot) rc/"$sname"/type
bundle_stage_depend_on "$if" "configure" "$sname"
}
new_if_bridge() {
local if="${1:?missing if}"
local sname="interface.$if.create"
bundle_stage_step_configure "$if" "$sname"
new_link "$if"
install -Dm644 <(
header_eb
header_if "$if"
echo 'ip link add $INTERFACE type bridge'
) rc/"$sname"/up
install -Dm644 <(linkdel "$if") rc/"$sname"/down
install -Dm644 <(echo oneshot) rc/"$sname"/type
depends "$sname" module.bridge
}
new_if_phys() {
local if="${1:?missing if}"
local sname="interface.$if.create"
bundle_stage_step_configure "$if" "$sname"
new_link "$if"
install -Dm644 <(
header_eb
header_if "$if"
echo 'bcnm-waitif 1 $INTERFACE'
) rc/"$sname"/up
install -Dm644 <(echo oneshot) rc/"$sname"/type
depends "$sname" bundle.hw-coldplug
}
new_if_lo() {
local if="${1:?missing if}"
local sname="interface.$if.create"
bundle_stage_step_configure "$if" "$sname"
new_link "$if"
install -Dm644 <(
header_eb
header_if "$if"
echo 'ip link add $INTERFACE type loopback'
) rc/"$sname"/up
install -Dm644 <(linkdel "$if") rc/"$sname"/down
install -Dm644 <(echo oneshot) rc/"$sname"/type
}
new_if_wg() {
local if="${1:?missing if}"
# Main service for creating interface
local sname="interface.$if.create"
bundle_stage_step_configure "$if" "$sname"
install -Dm644 <(
header_eb
header_if "$if"
echo 'ip link add $INTERFACE type wireguard'
) rc/"$sname"/up
install -Dm644 <(linkdel "$if") rc/"$sname"/down
install -Dm644 <(echo oneshot) rc/"$sname"/type
depends "$sname" module.wireguard
new_link "$if"
# Secondary service for loading config
if_wg_conf "$if"
}
new_if_vrf() {
local if="${1:?missing if}"
local table="${2:?missing table}"
local sname="interface.$if.create"
bundle_stage_step_configure "$if" "$sname"
install -Dm644 <(
header_eb
header_if "$if"
cat <<EOF
s6-envdir -i /etc/s6/env/interface.\${INTERFACE}
importas -i TABLE TABLE
EOF
echo 'ip link add $INTERFACE type vrf table $TABLE'
) rc/"$sname"/up
install -Dm644 <(linkdel "$if") rc/"$sname"/down
install -Dm644 <(echo oneshot) rc/"$sname"/type
install -Dm644 <(echo "$table") env/interface."$if"/TABLE
depends "$sname" module.vrf sysctl.net-ipv6-conf-all-keep_addr_on_down
new_link "$if"
}
new_if_vlan() {
local if="${1:?missing if}"
local vlan="$(echo $if | cut -d'-' -f2)"
local parent="$(echo $if | cut -d'-' -f1)"
local sname="interface.$if.create"
bundle_stage_step_configure "$if" "$sname"
install -Dm644 <(
header_eb
header_if "$if"
header_vlan "$vlan" "$parent"
echo 'ip link add link $PARENT name $INTERFACE type vlan id $VLAN'
) rc/"$sname"/up
install -Dm644 <(linkdel "$if") rc/"$sname"/down
install -Dm644 <(echo oneshot) rc/"$sname"/type
depends "$sname" module.8021q interface."$parent".create
new_link "$if"
}
new_if_bond() {
local if="${1:?missing if}"
local sname="interface.$if.create"
bundle_stage_step_configure "$if" "$sname"
install -Dm644 <(
header_eb
header_if "$if"
echo 'ip link add name $INTERFACE type bond'
) rc/"$sname"/up
install -Dm644 <(linkdel "$if") rc/"$sname"/down
install -Dm644 <(echo oneshot) rc/"$sname"/type
depends "$sname" module.bonding interface."$parent".create
new_link "$if"
}
#
# Interface configuration and properties
#
if_slave() {
local master="${1:?missing master}"
local slave="${2:?missing slave}"
local sname="interface.$master.slave.$slave"
bundle_stage_step_configure "$master" "$sname"
install -Dm644 <(
header_eb
cat <<EOF
define MASTERIF $master
define SLAVEIF $slave
EOF
echo 'ip link set dev $SLAVEIF master $MASTERIF'
) rc/"$sname"/up
install -Dm644 <(
header_eb
cat <<EOF
define SLAVEIF $slave
EOF
echo 'ip link set dev $SLAVEIF nomaster'
) rc/"$sname"/down
install -Dm644 <(echo oneshot) rc/"$sname"/type
depends "$sname" interface."$master".create interface."$slave".create
}
if_forward() {
local if="${1:?missing if}"
local fam="${2:-6}"
local val="${3:-1}"
if_sysctl "$if" "forwarding" "$val" '0' "$fam"
}
if_accept_ra() {
local if="${1:?missing if}"
local fam="6"
local val="${2:-2}"
if_sysctl "$if" "accept_ra" "$val" '0' "$fam"
}
if_sysctl() {
local if="${1:?missing if}"
local prop="${2:?missing property}"
local val_e="${3:-1}"
local val_d="${4:-0}"
local fam="${5:-6}"
local sname="sysctl.net-ipv$fam-conf-$if-$prop"
bundle_stage_step_configure "$if" "$sname"
install -Dm644 <(
header_eb
header_if "$if"
header_fam "$fam"
header_prop "$prop"
cat <<EOF
export VAL $val_e
s6-envdir -I /etc/s6/env/sysctl.net-ipv\${FAMILY}-conf-\${INTERFACE}-\${PROP}
export SYSCTL net/ipv\${FAMILY}/conf/\${INTERFACE}/\${PROP}
/usr/libexec/nnd/s6/sysctl
EOF
) rc/"$sname"/up
install -Dm644 <(
header_eb
header_if "$if"
header_fam "$fam"
header_prop "$prop"
cat <<EOF
export SYSCTL net/ipv\${FAMILY}/conf/\${INTERFACE}/\${PROP}
export VAL $val_d
/usr/libexec/nnd/s6/sysctl
EOF
) rc/"$sname"/down
install -Dm644 <(echo oneshot) rc/"$sname"/type
[ "$fam" = '6' ] && depends "$sname" module.ipv6
depends "$sname" mount.proc interface."$if".create
}
if_bridge_property() {
local prop="${1:?missing prop}"
local if="${2:?missing if}"
local state="${3:-1}"
local downstate="${4-0}"
local sname="interface.$if.bridge.$prop"
bundle_stage_step_configure "$if" "$sname"
install -Dm644 <(
header_eb
header_if "$if"
cat <<EOF
define PROP $prop
define STATE $state
EOF
echo 'ip link set $INTERFACE type bridge $PROP $STATE'
) rc/"$sname"/up
[ -z "$downstate" ] || install -Dm644 <(
header_eb
header_if "$if"
cat <<EOF
define PROP $prop
define STATE $downstate
EOF
echo 'ip link set $INTERFACE type bridge $PROP $STATE'
) rc/"$sname"/down
install -Dm644 <(echo oneshot) rc/"$sname"/type
# Might not be a requirement since STP is probably a hard dependency of bridges
[ "$prop" == 'stp_state' ] && depends "$sname" module.stp
depends "$sname" interface."$if".create
}
if_ether_address() {
local if="${1:?missing if}"
local addr="${2:?missing addr}"
local sname="interface.$if.ether.addr"
bundle_stage_step_configure "$if" "$sname"
install -Dm644 <(
header_eb
header_if "$if"
header_addr "$addr"
echo 'ip link set $INTERFACE address $ADDR'
) rc/"$sname"/up
install -Dm644 <(echo oneshot) rc/"$sname"/type
depends "$sname" interface."$if".create
}
if_wg_conf() {
local if="${1:?missing if}"
local sname="interface.$if.wg.config"
bundle_stage_step_configure "$if" "$sname"
install -Dm644 <(
header_eb
header_if "$if"
cat <<EOF
s6-envdir -I /etc/s6/env/interface.\${INTERFACE}.wg-config
importas -D /etc/wireguard/\${INTERFACE} CONFIG CONFIG
EOF
echo 'wg setconf $INTERFACE $CONFIG'
) rc/"$sname"/up
install -Dm644 <(echo oneshot) rc/"$sname"/type
depends "$sname" interface."$if".create
}
if_dhcp() {
local if="${1:?missing if}"
local sname="interface.$if.dhcp"
bundle_stage_step_ready "$if" "$sname"
install -Dm755 <(
header_eb
header_if "$if"
cat <<EOF
fdmove -c 2 1
udhcpc -i \$INTERFACE -f -S
EOF
) rc/"$sname"/run
install -Dm644 <(echo longrun) rc/"$sname"/type
install -Dm644 <(echo logger.udhcpc) rc/"$sname"/producer-for
depends "$sname" interface."$if".link syslogd
}
if_dhcpv6() {
local if="${1:?missing if}"
local sname="interface.$if.dhcpv6"
bundle_stage_step_ready "$if" "$sname"
install -Dm755 <(
header_eb
header_if "$if"
cat <<EOF
fdmove -c 2 1
udhcpc6 -i \$INTERFACE -f -S
EOF
) rc/"$sname"/run
install -Dm644 <(echo longrun) rc/"$sname"/type
install -Dm644 <(echo logger.udhcpc) rc/"$sname"/producer-for
depends "$sname" interface."$if".link syslogd
}
if_ip_addr() {
local if="${1:?missing if}"
local addr="${2:?missing addr}"
local fam="${3:-$(addrfam "$addr")}"
local addrn="$(echo "$addr" | sed 's/\//_/g')"
local sname="interface.$if.ip.addr.$fam.$addrn"
bundle_stage_step_ready "$if" "$sname"
install -Dm644 <(
header_eb
header_if "$if"
header_addr "$addr"
header_fam "$fam"
echo 'ip -${FAMILY} address add $ADDR dev $INTERFACE'
) rc/"$sname"/up
install -Dm644 <(
header_eb
header_if "$if"
header_addr "$addr"
header_fam "$fam"
echo 'ip -${FAMILY} address del $ADDR dev $INTERFACE'
) rc/"$sname"/down
install -Dm644 <(echo oneshot) rc/"$sname"/type
depends "$sname" interface."$if".link
}
if_route_addr() {
local if="${1:?missing if}"
local addr="${2:?missing addr}"
local fam="${3:-$(addrfam "$addr")}"
local addrn="$(echo "$addr" | sed 's/\//_/g')"
local sname="interface.$if.route.$fam.$addrn"
bundle_stage_step_ready "$if" "$sname" # Interface route may be added only if it's up
install -Dm644 <(
header_eb
header_if "$if"
header_addr "$addr"
header_fam "$fam"
echo 'ip -${FAMILY} route add to $ADDR dev $INTERFACE'
) rc/"$sname"/up
install -Dm644 <(
header_eb
header_if "$if"
header_addr "$addr"
header_fam "$fam"
echo 'ip -${FAMILY} route del to $ADDR dev $INTERFACE'
) rc/"$sname"/down
install -Dm644 <(echo oneshot) rc/"$sname"/type
depends "$sname" interface."$if".link
}
if_route_vrf_addr() {
local vrf="${1:?missing vrf}"
local if="${2:?missing if}"
local addr="${3:?missing addr}"
local fam="${4:-$(addrfam "$addr")}"
local addrn="$(echo "$addr" | sed 's/\//_/g')"
local sname="interface.$if.route.$vrf.$fam.$addrn"
bundle_stage_step_ready "$if" "$sname" # Interface route may be added only if it's up
install -Dm644 <(
header_eb
header_vrf "$vrf"
header_if "$if"
header_addr "$addr"
header_fam "$fam"
echo 'ip -${FAMILY} route add to $ADDR dev $INTERFACE vrf $VRF'
) rc/"$sname"/up
install -Dm644 <(
header_eb
header_vrf "$vrf"
header_if "$if"
header_addr "$addr"
header_fam "$fam"
echo 'ip -${FAMILY} route del to $ADDR dev $INTERFACE vrf $VRF'
) rc/"$sname"/down
install -Dm644 <(echo oneshot) rc/"$sname"/type
depends "$sname" interface."$if".link interface."$vrf".create
}
if_route_vrf_addr_gateway() {
local vrf="${1:?missing vrf}"
local if="${2:?missing if}"
local gateway="${3:?missing gw}"
local addr="${4:?missing addr}"
local fam="${5:-$(addrfam "$addr")}"
local gatewayn="$(echo "$gateway" | sed 's/\//_/g')"
local addrn="$(echo "$addr" | sed 's/\//_/g')"
local sname="interface.$if.gateway.$vrf.$fam.$gatewayn"
bundle_stage_step_ready "$if" "$sname" # Interface route may be added only if it's up
install -Dm644 <(
header_eb
header_vrf "$vrf"
header_if "$if"
header_addr "$gateway"
header_fam "$fam"
echo 'ip -${FAMILY} route add default via $ADDR dev $INTERFACE vrf $VRF'
) rc/"$sname"/up
install -Dm644 <(
header_eb
header_vrf "$vrf"
header_if "$if"
header_addr "$gateway"
header_fam "$fam"
echo 'ip -${FAMILY} route del default via $ADDR dev $INTERFACE vrf $VRF'
) rc/"$sname"/down
install -Dm644 <(echo oneshot) rc/"$sname"/type
depends "$sname" "interface.$if.route.$vrf.$fam.$addrn" interface."$if".link interface."$vrf".create
}
if_route_vrf_default_interface() {
local vrf="${1:?missing vrf}"
local if="${2:?missing if}"
local fam="${3:-6}"
local sname="interface.$vrf.route.$fam.default"
bundle_stage_step_configure "$vrf" "$sname"
install -Dm644 <(
header_eb
header_vrf "$vrf"
header_if "$if"
header_fam "$fam"
echo 'ip -${FAMILY} route add default dev $INTERFACE vrf $VRF'
) rc/"$sname"/up
install -Dm644 <(
header_eb
header_vrf "$vrf"
header_if "$if"
header_fam "$fam"
echo 'ip -${FAMILY} route del default dev $INTERFACE vrf $VRF'
) rc/"$sname"/down
install -Dm644 <(echo oneshot) rc/"$sname"/type
depends "$sname" interface."$if".link interface."$vrf".create
}
if_route_vrf_default_unreach() {
local if="${1:?missing if}"
local fam="${2:-6}"
local sname="interface.$if.route.$fam.unreach"
bundle_stage_step_configure "$if" "$sname"
install -Dm644 <(
header_eb
header_if "$if"
header_fam "$fam"
echo 'ip -${FAMILY} route add unreachable default metric 4096 vrf $INTERFACE'
) rc/"$sname"/up
install -Dm644 <(
header_eb
header_if "$if"
header_fam "$fam"
echo 'ip -${FAMILY} route del unreachable default metric 4096 vrf $INTERFACE'
) rc/"$sname"/down
install -Dm644 <(echo oneshot) rc/"$sname"/type
depends "$sname" interface."$if".create
}
if_route_vrf_sink_unreach() {
local if="${1:?missing if}"
local addr="${2:?missing addr}"
local fam="${3:-$(addrfam "$addr")}"
local addrn="$(echo "$addr" | sed 's/\//_/g')"
local sname="interface.$if.route.$fam.sink.$addrn"
bundle_stage_step_configure "$if" "$sname"
install -Dm644 <(
header_eb
header_if "$if"
header_addr "$addr"
header_fam "$fam"
echo 'ip -${FAMILY} route add unreachable $ADDR vrf $INTERFACE'
) rc/"$sname"/up
install -Dm644 <(
header_eb
header_if "$if"
header_addr "$addr"
header_fam "$fam"
echo 'ip -${FAMILY} route del unreachable $ADDR vrf $INTERFACE'
) rc/"$sname"/down
install -Dm644 <(echo oneshot) rc/"$sname"/type
depends "$sname" interface."$if".create
}
#
# Misc functionality
#
if_linkdepend() {
local master="${1:?missing master}"
local slave="${2:?missing slave}"
local sname="interface.$master.link"
depends "$sname" interface."$slave".link
}

9
nnd.go Normal file
View File

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

204
s6.go Normal file
View File

@ -0,0 +1,204 @@
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 {
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 && !errors.Is(err, os.ErrExist) {
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 {
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 {
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
}

13
services.go Normal file
View File

@ -0,0 +1,13 @@
package s6netdev
import (
"fmt"
)
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)
}

68
types.go Normal file
View File

@ -0,0 +1,68 @@
package s6netdev
import "net"
type (
RouteTable uint16
Metric uint16
VLAN uint16
)
type Property struct {
Key, Value, Default string
}
type Sysctl_IP struct {
V4, V6 []Property // 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 []byte // MAC address of interface (only valid for physical, bridges and VLAN)
Addresses []net.IP // Addresses to be assigned to interface
Routes []Route // Routes to be assigned to interface
DHCP struct {
V4, V6 bool // 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
Net net.IPNet
Via *net.IP
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"}},
}