2018-06-28 13:39:48 +02:00
|
|
|
|
// Copyright 2018 Google Inc.
|
|
|
|
|
//
|
|
|
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
|
// you may not use this file except in compliance with the License.
|
|
|
|
|
// You may obtain a copy of the License at
|
|
|
|
|
//
|
|
|
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
|
//
|
|
|
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
|
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
|
// See the License for the specific language governing permissions and
|
|
|
|
|
// limitations under the License.
|
|
|
|
|
|
2018-06-25 20:12:51 +02:00
|
|
|
|
// Package netconfig implements network configuration (interfaces, addresses, firewall rules, …).
|
2018-05-27 17:30:42 +02:00
|
|
|
|
package netconfig
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"encoding/json"
|
|
|
|
|
"fmt"
|
|
|
|
|
"io/ioutil"
|
|
|
|
|
"net"
|
2018-06-03 20:35:41 +02:00
|
|
|
|
"os"
|
2024-01-20 11:41:04 -08:00
|
|
|
|
"os/exec"
|
2020-06-12 17:53:13 -07:00
|
|
|
|
"path"
|
2018-05-27 17:30:42 +02:00
|
|
|
|
"path/filepath"
|
2018-06-14 22:25:39 +02:00
|
|
|
|
"regexp"
|
2018-05-27 17:30:42 +02:00
|
|
|
|
"strconv"
|
|
|
|
|
"strings"
|
2018-06-17 20:44:25 +02:00
|
|
|
|
"syscall"
|
2018-05-27 17:30:42 +02:00
|
|
|
|
|
2018-06-04 10:01:09 +02:00
|
|
|
|
"github.com/google/nftables"
|
2018-06-05 08:51:51 +02:00
|
|
|
|
"github.com/google/nftables/binaryutil"
|
2018-06-04 10:01:09 +02:00
|
|
|
|
"github.com/google/nftables/expr"
|
2019-01-06 15:23:09 +01:00
|
|
|
|
"github.com/google/renameio"
|
2022-08-30 21:58:55 +02:00
|
|
|
|
"github.com/mdlayher/ethtool"
|
2018-05-27 17:30:42 +02:00
|
|
|
|
"github.com/vishvananda/netlink"
|
2018-06-05 08:51:51 +02:00
|
|
|
|
"golang.org/x/sys/unix"
|
2018-05-27 17:30:42 +02:00
|
|
|
|
|
2018-07-09 08:54:04 +02:00
|
|
|
|
"github.com/rtr7/router7/internal/dhcp4"
|
|
|
|
|
"github.com/rtr7/router7/internal/dhcp6"
|
|
|
|
|
"github.com/rtr7/router7/internal/notify"
|
|
|
|
|
"github.com/rtr7/router7/internal/teelogger"
|
2018-05-27 17:30:42 +02:00
|
|
|
|
)
|
|
|
|
|
|
2018-06-03 20:47:11 +02:00
|
|
|
|
var log = teelogger.NewConsole()
|
|
|
|
|
|
2020-06-12 17:53:13 -07:00
|
|
|
|
var CmdRoot = "/user"
|
|
|
|
|
|
2018-05-27 17:30:42 +02:00
|
|
|
|
func subnetMaskSize(mask string) (int, error) {
|
|
|
|
|
parts := strings.Split(mask, ".")
|
|
|
|
|
if got, want := len(parts), 4; got != want {
|
|
|
|
|
return 0, fmt.Errorf("unexpected number of parts in subnet mask %q: got %d, want %d", mask, got, want)
|
|
|
|
|
}
|
|
|
|
|
numeric := make([]byte, len(parts))
|
|
|
|
|
for idx, part := range parts {
|
|
|
|
|
i, err := strconv.ParseUint(part, 0, 8)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return 0, err
|
|
|
|
|
}
|
|
|
|
|
numeric[idx] = byte(i)
|
|
|
|
|
}
|
|
|
|
|
ones, _ := net.IPv4Mask(numeric[0], numeric[1], numeric[2], numeric[3]).Size()
|
|
|
|
|
return ones, nil
|
|
|
|
|
}
|
|
|
|
|
|
2022-06-07 23:22:08 +02:00
|
|
|
|
func applyDhcp4(dir string, cfg InterfaceConfig) error {
|
2018-05-27 17:30:42 +02:00
|
|
|
|
b, err := ioutil.ReadFile(filepath.Join(dir, "dhcp4/wire/lease.json"))
|
|
|
|
|
if err != nil {
|
2018-06-02 17:38:17 +02:00
|
|
|
|
if os.IsNotExist(err) {
|
|
|
|
|
return nil // dhcp4 might not have obtained a lease yet
|
|
|
|
|
}
|
2018-05-27 17:30:42 +02:00
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
var got dhcp4.Config
|
|
|
|
|
if err := json.Unmarshal(b, &got); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
2020-09-12 19:58:47 +02:00
|
|
|
|
const linkName = "uplink0"
|
|
|
|
|
link, err := netlink.LinkByName(linkName)
|
2018-05-27 17:30:42 +02:00
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
2018-06-14 21:05:43 +02:00
|
|
|
|
if got.SubnetMask == "" {
|
|
|
|
|
return fmt.Errorf("invalid DHCP lease: no subnet mask present")
|
|
|
|
|
}
|
|
|
|
|
|
2018-05-27 17:30:42 +02:00
|
|
|
|
subnetSize, err := subnetMaskSize(got.SubnetMask)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
2020-09-12 19:58:47 +02:00
|
|
|
|
gotAddr := fmt.Sprintf("%s/%d", got.ClientIP, subnetSize)
|
|
|
|
|
addr, err := netlink.ParseAddr(gotAddr)
|
2018-05-27 17:30:42 +02:00
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
h, err := netlink.NewHandle()
|
|
|
|
|
if err != nil {
|
|
|
|
|
return fmt.Errorf("netlink.NewHandle: %v", err)
|
|
|
|
|
}
|
2018-07-22 23:07:23 +02:00
|
|
|
|
defer h.Delete()
|
2020-09-12 19:58:47 +02:00
|
|
|
|
log.Printf("replacing address %v on %v", addr, linkName)
|
2018-06-04 10:01:09 +02:00
|
|
|
|
if err := h.AddrReplace(link, addr); err != nil {
|
2020-09-12 19:58:47 +02:00
|
|
|
|
return fmt.Errorf("AddrReplace(%v, %v): %v", linkName, addr, err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
addrs, err := h.AddrList(link, netlink.FAMILY_V4)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return fmt.Errorf("AddrList(%v): %v", linkName, err)
|
|
|
|
|
}
|
|
|
|
|
for _, addr := range addrs {
|
|
|
|
|
ipnet := addr.IPNet.String() // e.g. "85.195.199.99/25"
|
|
|
|
|
if ipnet == gotAddr {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
log.Printf("de-configuring old IP address %s from %v", ipnet, linkName)
|
|
|
|
|
if err := h.AddrDel(link, &addr); err != nil {
|
|
|
|
|
return fmt.Errorf("AddrDel(%v, %v): %v", linkName, addr, err)
|
|
|
|
|
}
|
2018-05-27 17:30:42 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// from include/uapi/linux/rtnetlink.h
|
|
|
|
|
const (
|
|
|
|
|
RTPROT_STATIC = 4
|
|
|
|
|
RTPROT_DHCP = 16
|
|
|
|
|
)
|
|
|
|
|
|
2018-06-12 09:29:53 +02:00
|
|
|
|
if err := h.RouteReplace(&netlink.Route{
|
2018-05-27 17:30:42 +02:00
|
|
|
|
LinkIndex: link.Attrs().Index,
|
|
|
|
|
Dst: &net.IPNet{
|
|
|
|
|
IP: net.ParseIP(got.Router),
|
|
|
|
|
Mask: net.CIDRMask(32, 32),
|
|
|
|
|
},
|
|
|
|
|
Src: net.ParseIP(got.ClientIP),
|
|
|
|
|
Scope: netlink.SCOPE_LINK,
|
|
|
|
|
Protocol: RTPROT_DHCP,
|
|
|
|
|
}); err != nil {
|
2018-06-12 09:29:53 +02:00
|
|
|
|
return fmt.Errorf("RouteReplace(router): %v", err)
|
2018-05-27 17:30:42 +02:00
|
|
|
|
}
|
|
|
|
|
|
2022-06-07 23:22:08 +02:00
|
|
|
|
if defaultViaWireguard(cfg) {
|
|
|
|
|
// The default route is on a WireGuard interface, so do not install the
|
|
|
|
|
// default route from the DHCP reply. Instead, set up a host route for
|
|
|
|
|
// the WireGuard endpoint(s).
|
|
|
|
|
|
|
|
|
|
log.Printf("IPv4 traffic is routed via WireGuard, setting host route instead of default route")
|
|
|
|
|
|
|
|
|
|
b, err := ioutil.ReadFile(filepath.Join(dir, "wireguard.json"))
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
var wgcfg wireguardInterfaces
|
|
|
|
|
if err := json.Unmarshal(b, &wgcfg); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for _, iface := range wgcfg.Interfaces {
|
|
|
|
|
for _, p := range iface.Peers {
|
|
|
|
|
addr, err := net.ResolveUDPAddr("udp", p.Endpoint)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
log.Printf(" WireGuard endpoint %s", addr.IP)
|
|
|
|
|
|
|
|
|
|
router := net.ParseIP(got.Router)
|
|
|
|
|
if addr.IP.Equal(router) {
|
|
|
|
|
continue // endpoint == router, no route required
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if err := h.RouteReplace(&netlink.Route{
|
|
|
|
|
LinkIndex: link.Attrs().Index,
|
|
|
|
|
Dst: &net.IPNet{
|
|
|
|
|
IP: addr.IP,
|
|
|
|
|
Mask: net.CIDRMask(32, 32),
|
|
|
|
|
},
|
|
|
|
|
Gw: net.ParseIP(got.Router),
|
|
|
|
|
Src: net.ParseIP(got.ClientIP),
|
|
|
|
|
Protocol: RTPROT_DHCP,
|
|
|
|
|
}); err != nil {
|
|
|
|
|
return fmt.Errorf("RouteReplace(default): %v", err)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
if err := h.RouteReplace(&netlink.Route{
|
|
|
|
|
LinkIndex: link.Attrs().Index,
|
|
|
|
|
Dst: &net.IPNet{
|
|
|
|
|
IP: net.ParseIP("0.0.0.0"),
|
|
|
|
|
Mask: net.CIDRMask(0, 32),
|
|
|
|
|
},
|
|
|
|
|
Gw: net.ParseIP(got.Router),
|
|
|
|
|
Src: net.ParseIP(got.ClientIP),
|
|
|
|
|
Protocol: RTPROT_DHCP,
|
|
|
|
|
}); err != nil {
|
|
|
|
|
return fmt.Errorf("RouteReplace(default): %v", err)
|
|
|
|
|
}
|
2018-05-27 17:30:42 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
2022-06-07 23:22:08 +02:00
|
|
|
|
func defaultViaWireguard(cfg InterfaceConfig) bool {
|
|
|
|
|
for _, iface := range cfg.Interfaces {
|
|
|
|
|
if !strings.HasPrefix(iface.Name, "wg") {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
for _, route := range iface.ExtraRoutes {
|
|
|
|
|
_, n, err := net.ParseCIDR(route.Destination)
|
|
|
|
|
if err != nil {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
ones, bits := n.Mask.Size()
|
|
|
|
|
if n.IP.Equal(net.IPv4zero) && ones == 0 && bits == 32 {
|
|
|
|
|
return true
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
|
2018-06-03 20:35:41 +02:00
|
|
|
|
func applyDhcp6(dir string) error {
|
2018-05-27 17:30:42 +02:00
|
|
|
|
b, err := ioutil.ReadFile(filepath.Join(dir, "dhcp6/wire/lease.json"))
|
|
|
|
|
if err != nil {
|
2018-06-02 17:38:17 +02:00
|
|
|
|
if os.IsNotExist(err) {
|
|
|
|
|
return nil // dhcp6 might not have obtained a lease yet
|
|
|
|
|
}
|
2018-05-27 17:30:42 +02:00
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
var got dhcp6.Config
|
|
|
|
|
if err := json.Unmarshal(b, &got); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
2018-06-03 20:35:41 +02:00
|
|
|
|
link, err := netlink.LinkByName("lan0")
|
2018-05-27 17:30:42 +02:00
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for _, prefix := range got.Prefixes {
|
|
|
|
|
// pick the first address of the prefix, e.g. address 2a02:168:4a00::1
|
|
|
|
|
// for prefix 2a02:168:4a00::/48
|
|
|
|
|
prefix.IP[len(prefix.IP)-1] = 1
|
2018-05-28 09:53:54 +02:00
|
|
|
|
// Use the first /64 subnet within larger prefixes
|
|
|
|
|
if ones, bits := prefix.Mask.Size(); ones < 64 {
|
|
|
|
|
prefix.Mask = net.CIDRMask(64, bits)
|
|
|
|
|
}
|
2018-05-27 17:30:42 +02:00
|
|
|
|
addr, err := netlink.ParseAddr(prefix.String())
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
2018-06-14 08:27:08 +02:00
|
|
|
|
if err := netlink.AddrReplace(link, addr); err != nil {
|
|
|
|
|
return fmt.Errorf("AddrReplace(%v): %v", addr, err)
|
2018-05-27 17:30:42 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
2022-06-06 14:17:44 +02:00
|
|
|
|
type Route struct {
|
|
|
|
|
Destination string `json:"destination"` // e.g. 2a02:168:4a00:22::/64
|
|
|
|
|
Gateway string `json:"gateway"` // e.g. fe80::1
|
|
|
|
|
}
|
|
|
|
|
|
2018-05-27 17:30:42 +02:00
|
|
|
|
type InterfaceDetails struct {
|
2022-06-06 13:58:57 +02:00
|
|
|
|
HardwareAddr string `json:"hardware_addr"` // e.g. dc:9b:9c:ee:72:fd
|
|
|
|
|
SpoofHardwareAddr string `json:"spoof_hardware_addr"` // e.g. dc:9b:9c:ee:72:fd
|
|
|
|
|
Name string `json:"name"` // e.g. uplink0, or lan0
|
|
|
|
|
Addr string `json:"addr"` // e.g. 192.168.42.1/24
|
|
|
|
|
ExtraAddrs []string `json:"extra_addrs"` // e.g. ["192.168.23.1/24"]
|
2022-06-06 14:17:44 +02:00
|
|
|
|
ExtraRoutes []Route `json:"extra_routes"`
|
2022-06-15 23:19:43 +02:00
|
|
|
|
MTU int `json:"mtu"` // e.g. 1492 for PPPoE connections
|
2022-08-30 21:58:55 +02:00
|
|
|
|
// FEC optionally allows configuring forward error correction, e.g. RS for
|
|
|
|
|
// reed-solomon forward error correction, or Off to disable.
|
|
|
|
|
//
|
|
|
|
|
// Some network card and SFP module combinations (e.g. Mellanox ConnectX-4
|
|
|
|
|
// with a Flexoptix P.B1625G.10.AD) need to explicitly be configured to use
|
|
|
|
|
// RS forward error correction, otherwise they won’t link.
|
|
|
|
|
FEC string `json:"fec"`
|
2018-05-27 17:30:42 +02:00
|
|
|
|
}
|
|
|
|
|
|
2021-06-06 12:02:18 +02:00
|
|
|
|
type BridgeDetails struct {
|
|
|
|
|
Name string `json:"name"` // e.g. br0 or lan0
|
|
|
|
|
InterfaceHardwareAddrs []string `json:"interface_hardware_addrs"`
|
|
|
|
|
}
|
|
|
|
|
|
2018-05-27 17:30:42 +02:00
|
|
|
|
type InterfaceConfig struct {
|
|
|
|
|
Interfaces []InterfaceDetails `json:"interfaces"`
|
2021-06-06 12:02:18 +02:00
|
|
|
|
Bridges []BridgeDetails `json:"bridges"`
|
2018-05-27 17:30:42 +02:00
|
|
|
|
}
|
|
|
|
|
|
2019-02-19 09:33:29 +01:00
|
|
|
|
// Interface returns the InterfaceDetails configured for interface ifname in
|
2018-06-04 08:02:28 +02:00
|
|
|
|
// interfaces.json.
|
2019-02-19 09:33:29 +01:00
|
|
|
|
func Interface(dir, ifname string) (InterfaceDetails, error) {
|
2018-06-04 08:02:28 +02:00
|
|
|
|
fn := filepath.Join(dir, "interfaces.json")
|
|
|
|
|
b, err := ioutil.ReadFile(fn)
|
|
|
|
|
if err != nil {
|
2019-02-19 09:33:29 +01:00
|
|
|
|
return InterfaceDetails{}, err
|
2018-06-04 08:02:28 +02:00
|
|
|
|
}
|
|
|
|
|
var cfg InterfaceConfig
|
|
|
|
|
if err := json.Unmarshal(b, &cfg); err != nil {
|
2019-02-19 09:33:29 +01:00
|
|
|
|
return InterfaceDetails{}, err
|
2018-06-04 08:02:28 +02:00
|
|
|
|
}
|
|
|
|
|
for _, details := range cfg.Interfaces {
|
|
|
|
|
if details.Name != ifname {
|
|
|
|
|
continue
|
|
|
|
|
}
|
2019-02-19 09:33:29 +01:00
|
|
|
|
return details, nil
|
|
|
|
|
}
|
|
|
|
|
return InterfaceDetails{}, fmt.Errorf("%s does not configure interface %q", fn, ifname)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// LinkAddress returns the IP address configured for the interface ifname in
|
|
|
|
|
// interfaces.json.
|
|
|
|
|
func LinkAddress(dir, ifname string) (net.IP, error) {
|
|
|
|
|
iface, err := Interface(dir, ifname)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
2018-06-04 08:02:28 +02:00
|
|
|
|
}
|
2019-02-19 09:33:29 +01:00
|
|
|
|
ip, _, err := net.ParseCIDR(iface.Addr)
|
|
|
|
|
return ip, err
|
2018-06-04 08:02:28 +02:00
|
|
|
|
}
|
|
|
|
|
|
2021-06-12 18:25:37 +02:00
|
|
|
|
func applyBridges(cfg *InterfaceConfig) error {
|
2021-06-06 12:02:18 +02:00
|
|
|
|
for _, bridge := range cfg.Bridges {
|
|
|
|
|
if _, err := netlink.LinkByName(bridge.Name); err != nil {
|
2021-06-12 18:25:37 +02:00
|
|
|
|
log.Printf("creating bridge %s", bridge.Name)
|
2021-06-06 12:02:18 +02:00
|
|
|
|
link := &netlink.Bridge{LinkAttrs: netlink.LinkAttrs{Name: bridge.Name}}
|
|
|
|
|
if err := netlink.LinkAdd(link); err != nil {
|
|
|
|
|
return fmt.Errorf("netlink.LinkAdd: %v", err)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
interfaces := make(map[string]bool)
|
|
|
|
|
for _, hwaddr := range bridge.InterfaceHardwareAddrs {
|
|
|
|
|
interfaces[hwaddr] = true
|
|
|
|
|
}
|
|
|
|
|
bridgeLink, err := netlink.LinkByName(bridge.Name)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return fmt.Errorf("LinkByName(%s): %v", bridge.Name, err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
links, err := netlink.LinkList()
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
for _, l := range links {
|
|
|
|
|
attr := l.Attrs()
|
|
|
|
|
addr := attr.HardwareAddr.String()
|
|
|
|
|
if addr == "" {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
if !interfaces[addr] {
|
|
|
|
|
continue
|
|
|
|
|
}
|
2021-06-12 22:24:38 +02:00
|
|
|
|
if attr.Name == bridge.Name {
|
|
|
|
|
// Don’t try to add the bridge to itself: the bridge will take
|
|
|
|
|
// the MAC address of the first interface.
|
|
|
|
|
continue
|
|
|
|
|
}
|
2021-06-06 12:02:18 +02:00
|
|
|
|
log.Printf("adding interface %s to bridge %s", attr.Name, bridge.Name)
|
|
|
|
|
if err := netlink.LinkSetMaster(l, bridgeLink); err != nil {
|
|
|
|
|
return fmt.Errorf("LinkSetMaster(%s): %v", attr.Name, err)
|
|
|
|
|
}
|
|
|
|
|
if attr.OperState != netlink.OperUp {
|
|
|
|
|
log.Printf("setting interface %s up", attr.Name)
|
|
|
|
|
if err := netlink.LinkSetUp(l); err != nil {
|
|
|
|
|
return fmt.Errorf("LinkSetUp(%s): %v", attr.Name, err)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
if attr := bridgeLink.Attrs(); attr.OperState != netlink.OperUp {
|
|
|
|
|
log.Printf("setting interface %s up", attr.Name)
|
|
|
|
|
if err := netlink.LinkSetUp(bridgeLink); err != nil {
|
|
|
|
|
return fmt.Errorf("LinkSetUp(%s): %v", attr.Name, err)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2021-06-12 18:25:37 +02:00
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
2022-08-30 21:58:55 +02:00
|
|
|
|
func applyInterfaceFEC(details InterfaceDetails) error {
|
|
|
|
|
if details.FEC == "" {
|
|
|
|
|
return nil // nothing to do
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
desired := ethtool.FECModes(unix.ETHTOOL_FEC_RS)
|
|
|
|
|
switch strings.ToLower(details.FEC) {
|
|
|
|
|
case "rs":
|
|
|
|
|
desired = unix.ETHTOOL_FEC_RS
|
|
|
|
|
case "baser":
|
|
|
|
|
desired = unix.ETHTOOL_FEC_BASER
|
|
|
|
|
case "off":
|
|
|
|
|
desired = unix.ETHTOOL_FEC_OFF
|
|
|
|
|
case "none":
|
|
|
|
|
desired = unix.ETHTOOL_FEC_NONE
|
|
|
|
|
case "llrs":
|
|
|
|
|
desired = unix.ETHTOOL_FEC_LLRS
|
|
|
|
|
case "auto":
|
|
|
|
|
desired = 0
|
|
|
|
|
default:
|
|
|
|
|
return fmt.Errorf("unknown FEC value %q, expected one of RS, BaseR, LLRS, Auto, None, Off", details.FEC)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
cl, err := ethtool.New()
|
2018-05-27 17:30:42 +02:00
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
2022-08-30 21:58:55 +02:00
|
|
|
|
defer cl.Close()
|
|
|
|
|
|
|
|
|
|
li, err := cl.LinkInfo(ethtool.Interface{Name: details.Name})
|
|
|
|
|
if err != nil {
|
|
|
|
|
return fmt.Errorf("LinkInfo(%s): %v", details.Name, err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fec, err := cl.FEC(li.Interface)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return fmt.Errorf("FEC(%s): %v", li.Interface.Name, err)
|
|
|
|
|
}
|
|
|
|
|
log.Printf("FEC supported/configured: [%v], active: %v", fec.Supported(), fec.Active)
|
|
|
|
|
// fec.Active is not set when there is no link, so we compare
|
|
|
|
|
// supported/configured instead.
|
|
|
|
|
if fec.Supported() == desired {
|
|
|
|
|
return nil // already matching the desired configuration
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
log.Printf("setting FEC to %v", desired)
|
|
|
|
|
if err := cl.SetFEC(ethtool.FEC{
|
|
|
|
|
Interface: li.Interface,
|
|
|
|
|
Modes: desired,
|
|
|
|
|
Auto: strings.ToLower(details.FEC) == "auto",
|
|
|
|
|
}); err != nil {
|
2018-05-27 17:30:42 +02:00
|
|
|
|
return err
|
|
|
|
|
}
|
2022-08-30 21:58:55 +02:00
|
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
2022-06-07 23:22:08 +02:00
|
|
|
|
func applyInterfaces(dir, root string, cfg InterfaceConfig) error {
|
2018-11-26 18:29:03 +01:00
|
|
|
|
byName := make(map[string]InterfaceDetails)
|
2018-05-27 17:30:42 +02:00
|
|
|
|
byHardwareAddr := make(map[string]InterfaceDetails)
|
|
|
|
|
for _, details := range cfg.Interfaces {
|
|
|
|
|
byHardwareAddr[details.HardwareAddr] = details
|
2018-06-04 08:30:57 +02:00
|
|
|
|
if spoof := details.SpoofHardwareAddr; spoof != "" {
|
|
|
|
|
byHardwareAddr[spoof] = details
|
|
|
|
|
}
|
2018-11-26 18:29:03 +01:00
|
|
|
|
byName[details.Name] = details
|
2018-05-27 17:30:42 +02:00
|
|
|
|
}
|
2021-06-12 18:25:37 +02:00
|
|
|
|
|
|
|
|
|
if err := applyBridges(&cfg); err != nil {
|
|
|
|
|
log.Printf("applyBridges: %v", err)
|
|
|
|
|
}
|
2021-06-06 12:02:18 +02:00
|
|
|
|
|
2018-05-27 17:30:42 +02:00
|
|
|
|
links, err := netlink.LinkList()
|
2020-05-09 17:04:31 -04:00
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
2018-05-27 17:30:42 +02:00
|
|
|
|
for _, l := range links {
|
|
|
|
|
attr := l.Attrs()
|
2018-06-03 20:35:41 +02:00
|
|
|
|
// TODO: prefix log line with details about the interface.
|
|
|
|
|
// link &{LinkAttrs:{Index:2 MTU:1500 TxQLen:1000 Name:eth0 HardwareAddr:00:0d:b9:49:70:18 Flags:broadcast|multicast RawFlags:4098 ParentIndex:0 MasterIndex:0 Namespace:<nil> Alias: Statistics:0xc4200f45f8 Promisc:0 Xdp:0xc4200ca180 EncapType:ether Protinfo:<nil> OperState:down NetNsID:0 NumTxQueues:0 NumRxQueues:0 Vfs:[]}}, attr &{Index:2 MTU:1500 TxQLen:1000 Name:eth0 HardwareAddr:00:0d:b9:49:70:18 Flags:broadcast|multicast RawFlags:4098 ParentIndex:0 MasterIndex:0 Namespace:<nil> Alias: Statistics:0xc4200f45f8 Promisc:0 Xdp:0xc4200ca180 EncapType:ether Protinfo:<nil> OperState:down NetNsID:0 NumTxQueues:0 NumRxQueues:0 Vfs:[]}
|
|
|
|
|
|
2018-11-26 18:29:03 +01:00
|
|
|
|
var (
|
|
|
|
|
details InterfaceDetails
|
|
|
|
|
ok bool
|
|
|
|
|
)
|
2018-06-03 20:35:41 +02:00
|
|
|
|
addr := attr.HardwareAddr.String()
|
2018-11-26 18:29:03 +01:00
|
|
|
|
if addr == "" {
|
|
|
|
|
details, ok = byName[attr.Name]
|
|
|
|
|
if !ok {
|
2018-06-03 20:35:41 +02:00
|
|
|
|
continue // not a configurable interface (e.g. sit0)
|
|
|
|
|
}
|
2018-11-26 18:29:03 +01:00
|
|
|
|
} else {
|
|
|
|
|
details, ok = byHardwareAddr[addr]
|
2021-06-06 12:02:18 +02:00
|
|
|
|
if !ok {
|
|
|
|
|
details, ok = byName[attr.Name]
|
|
|
|
|
}
|
2018-11-26 18:29:03 +01:00
|
|
|
|
}
|
|
|
|
|
if !ok {
|
|
|
|
|
log.Printf("no config for interface %s/%s", attr.Name, addr)
|
2018-05-27 17:30:42 +02:00
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
log.Printf("apply details %+v", details)
|
|
|
|
|
if attr.Name != details.Name {
|
|
|
|
|
if err := netlink.LinkSetName(l, details.Name); err != nil {
|
|
|
|
|
return fmt.Errorf("LinkSetName(%q): %v", details.Name, err)
|
|
|
|
|
}
|
|
|
|
|
attr.Name = details.Name
|
|
|
|
|
}
|
|
|
|
|
|
2022-06-15 23:19:43 +02:00
|
|
|
|
if details.MTU != 0 {
|
|
|
|
|
if err := netlink.LinkSetMTU(l, details.MTU); err != nil {
|
|
|
|
|
return fmt.Errorf("LinkSetMTU(%d): %v", details.MTU, err)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-06-04 08:30:57 +02:00
|
|
|
|
if spoof := details.SpoofHardwareAddr; spoof != "" {
|
|
|
|
|
hwaddr, err := net.ParseMAC(spoof)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return fmt.Errorf("ParseMAC(%q): %v", spoof, err)
|
|
|
|
|
}
|
|
|
|
|
if err := netlink.LinkSetHardwareAddr(l, hwaddr); err != nil {
|
|
|
|
|
return fmt.Errorf("LinkSetHardwareAddr(%v): %v", hwaddr, err)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-08-30 21:58:55 +02:00
|
|
|
|
if err := applyInterfaceFEC(details); err != nil {
|
|
|
|
|
// TODO: turn this into returning an error once proven stable
|
|
|
|
|
log.Printf("applyInterfaceFEC: %v", err)
|
|
|
|
|
}
|
|
|
|
|
|
2018-05-27 17:30:42 +02:00
|
|
|
|
if attr.OperState != netlink.OperUp {
|
|
|
|
|
// Set the interface to up, which is required by all other configuration.
|
|
|
|
|
if err := netlink.LinkSetUp(l); err != nil {
|
|
|
|
|
return fmt.Errorf("LinkSetUp(%s): %v", attr.Name, err)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if details.Addr != "" {
|
|
|
|
|
addr, err := netlink.ParseAddr(details.Addr)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return fmt.Errorf("ParseAddr(%q): %v", details.Addr, err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if err := netlink.AddrReplace(l, addr); err != nil {
|
|
|
|
|
return fmt.Errorf("AddrReplace(%s, %v): %v", attr.Name, addr, err)
|
|
|
|
|
}
|
2018-06-03 20:47:11 +02:00
|
|
|
|
|
|
|
|
|
if details.Name == "lan0" {
|
|
|
|
|
b := []byte("nameserver " + addr.IP.String() + "\n")
|
2018-06-22 17:59:23 +02:00
|
|
|
|
fn := filepath.Join(root, "tmp", "resolv.conf")
|
2018-06-03 20:47:11 +02:00
|
|
|
|
if err := os.Remove(fn); err != nil && !os.IsNotExist(err) {
|
|
|
|
|
return err
|
|
|
|
|
}
|
2019-01-06 15:23:09 +01:00
|
|
|
|
if err := renameio.WriteFile(fn, b, 0644); err != nil {
|
2018-06-03 20:47:11 +02:00
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
}
|
2018-05-27 17:30:42 +02:00
|
|
|
|
}
|
2022-06-06 13:58:57 +02:00
|
|
|
|
|
|
|
|
|
for _, addr := range details.ExtraAddrs {
|
2022-06-06 14:17:44 +02:00
|
|
|
|
log.Printf("replacing extra address %v on %v", addr, attr.Name)
|
2022-06-06 13:58:57 +02:00
|
|
|
|
addr, err := netlink.ParseAddr(addr)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return fmt.Errorf("ParseAddr(%q): %v", addr, err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if err := netlink.AddrReplace(l, addr); err != nil {
|
|
|
|
|
return fmt.Errorf("AddrReplace(%s, %v): %v", attr.Name, addr, err)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-06-06 14:17:44 +02:00
|
|
|
|
for _, route := range details.ExtraRoutes {
|
|
|
|
|
_, dst, err := net.ParseCIDR(route.Destination)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return fmt.Errorf("ParseCIDR(%q): %v", route.Destination, err)
|
|
|
|
|
}
|
|
|
|
|
r := &netlink.Route{Dst: dst}
|
|
|
|
|
if route.Gateway != "" {
|
|
|
|
|
r.Gw = net.ParseIP(route.Gateway)
|
|
|
|
|
}
|
|
|
|
|
r.LinkIndex = attr.Index
|
|
|
|
|
|
|
|
|
|
log.Printf("replacing extra route %v on %v", r, attr.Name)
|
|
|
|
|
|
|
|
|
|
if err := netlink.RouteReplace(r); err != nil {
|
|
|
|
|
return fmt.Errorf("RouteReplace(%v): %v", r, err)
|
|
|
|
|
}
|
|
|
|
|
}
|
2018-05-27 17:30:42 +02:00
|
|
|
|
}
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
2020-02-15 23:37:29 +01:00
|
|
|
|
func nfifname(n string) []byte {
|
2018-06-05 08:48:30 +02:00
|
|
|
|
b := make([]byte, 16)
|
|
|
|
|
copy(b, []byte(n+"\x00"))
|
|
|
|
|
return b
|
|
|
|
|
}
|
|
|
|
|
|
2022-03-08 09:32:09 +01:00
|
|
|
|
// matchUplinkIP is conceptually equivalent to "ip daddr <uplink0-ip>", but
|
|
|
|
|
// without actually using the IP address of the uplink0 interface (which would
|
|
|
|
|
// mean that rules need to change when the IP address changes).
|
|
|
|
|
//
|
|
|
|
|
// Instead, it uses “fib daddr type local” to match all locally-configured IP
|
|
|
|
|
// addresses and then excludes the loopback and LAN IP addresses.
|
|
|
|
|
func matchUplinkIP() []expr.Any {
|
|
|
|
|
return []expr.Any{
|
|
|
|
|
// [ payload load 4b @ network header + 16 => reg 1 ]
|
|
|
|
|
&expr.Payload{
|
|
|
|
|
DestRegister: 1,
|
|
|
|
|
Base: expr.PayloadBaseNetworkHeader,
|
|
|
|
|
Offset: 16, // TODO
|
|
|
|
|
Len: 4, // TODO
|
|
|
|
|
},
|
|
|
|
|
// [ bitwise reg 1 = (reg=1 & 0x000000ff ) ^ 0x00000000 ]
|
|
|
|
|
&expr.Bitwise{
|
|
|
|
|
DestRegister: 1,
|
|
|
|
|
SourceRegister: 1,
|
|
|
|
|
Len: 4,
|
|
|
|
|
Mask: []byte{0xff, 0x00, 0x00, 0x00}, // 255.0.0.0, i.e. /8
|
|
|
|
|
Xor: []byte{0x00, 0x00, 0x00, 0x00},
|
|
|
|
|
},
|
|
|
|
|
// [ cmp neq reg 1 0x0000007f ]
|
|
|
|
|
&expr.Cmp{
|
|
|
|
|
Op: expr.CmpOpNeq,
|
|
|
|
|
Register: 1,
|
|
|
|
|
Data: []byte{0x7f, 0x00, 0x00, 0x00},
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
// [ payload load 4b @ network header + 16 => reg 1 ]
|
|
|
|
|
&expr.Payload{
|
|
|
|
|
DestRegister: 1,
|
|
|
|
|
Base: expr.PayloadBaseNetworkHeader,
|
|
|
|
|
Offset: 16, // TODO
|
|
|
|
|
Len: 4, // TODO
|
|
|
|
|
},
|
|
|
|
|
// [ bitwise reg 1 = (reg=1 & 0x00ffffff ) ^ 0x00000000 ]
|
|
|
|
|
&expr.Bitwise{
|
|
|
|
|
DestRegister: 1,
|
|
|
|
|
SourceRegister: 1,
|
|
|
|
|
Len: 4,
|
|
|
|
|
Mask: []byte{0xff, 0xff, 0xff, 0x00}, // 255.255.255.0, i.e. /24
|
|
|
|
|
Xor: []byte{0x00, 0x00, 0x00, 0x00},
|
|
|
|
|
},
|
|
|
|
|
// [ cmp neq reg 1 0x0000000a ]
|
|
|
|
|
&expr.Cmp{
|
|
|
|
|
Op: expr.CmpOpNeq,
|
|
|
|
|
Register: 1,
|
|
|
|
|
Data: []byte{0x0a, 0x00, 0x00, 0x00},
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
// [ fib daddr type => reg 1 ]
|
|
|
|
|
&expr.Fib{
|
|
|
|
|
Register: 1,
|
|
|
|
|
FlagDADDR: true,
|
|
|
|
|
ResultADDRTYPE: true,
|
|
|
|
|
},
|
|
|
|
|
// [ cmp eq reg 1 0x00000002 ]
|
|
|
|
|
&expr.Cmp{
|
|
|
|
|
Op: expr.CmpOpEq,
|
|
|
|
|
Register: 1,
|
|
|
|
|
Data: []byte{0x02, 0x00, 0x00, 0x00},
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-02-15 23:37:29 +01:00
|
|
|
|
func portForwardExpr(ifname string, proto uint8, portMin, portMax uint16, dest net.IP, dportMin, dportMax uint16) []expr.Any {
|
2018-06-14 22:25:39 +02:00
|
|
|
|
var cmp []expr.Any
|
|
|
|
|
if portMin == portMax {
|
|
|
|
|
cmp = []expr.Any{
|
|
|
|
|
// [ cmp eq reg 1 0x0000e60f ]
|
|
|
|
|
&expr.Cmp{
|
|
|
|
|
Op: expr.CmpOpEq,
|
|
|
|
|
Register: 1,
|
|
|
|
|
Data: binaryutil.BigEndian.PutUint16(portMin),
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
cmp = []expr.Any{
|
|
|
|
|
// [ cmp gte reg 1 0x0000e60f ]
|
|
|
|
|
&expr.Cmp{
|
|
|
|
|
Op: expr.CmpOpGte,
|
|
|
|
|
Register: 1,
|
|
|
|
|
Data: binaryutil.BigEndian.PutUint16(portMin),
|
|
|
|
|
},
|
|
|
|
|
// [ cmp lte reg 1 0x0000fa0f ]
|
|
|
|
|
&expr.Cmp{
|
|
|
|
|
Op: expr.CmpOpLte,
|
|
|
|
|
Register: 1,
|
|
|
|
|
Data: binaryutil.BigEndian.PutUint16(portMax),
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-03-08 09:32:09 +01:00
|
|
|
|
ex := append(matchUplinkIP(),
|
2018-06-05 08:51:51 +02:00
|
|
|
|
// [ meta load l4proto => reg 1 ]
|
|
|
|
|
&expr.Meta{Key: expr.MetaKeyL4PROTO, Register: 1},
|
|
|
|
|
// [ cmp eq reg 1 0x00000006 ]
|
|
|
|
|
&expr.Cmp{
|
|
|
|
|
Op: expr.CmpOpEq,
|
|
|
|
|
Register: 1,
|
2018-06-14 21:05:43 +02:00
|
|
|
|
Data: []byte{proto},
|
2018-06-05 08:51:51 +02:00
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
// [ payload load 2b @ transport header + 2 => reg 1 ]
|
|
|
|
|
&expr.Payload{
|
|
|
|
|
DestRegister: 1,
|
|
|
|
|
Base: expr.PayloadBaseTransportHeader,
|
|
|
|
|
Offset: 2, // TODO
|
|
|
|
|
Len: 2, // TODO
|
2022-03-08 09:32:09 +01:00
|
|
|
|
})
|
2018-06-14 22:25:39 +02:00
|
|
|
|
ex = append(ex, cmp...)
|
|
|
|
|
ex = append(ex,
|
2018-06-05 08:51:51 +02:00
|
|
|
|
// [ immediate reg 1 0x0217a8c0 ]
|
|
|
|
|
&expr.Immediate{
|
|
|
|
|
Register: 1,
|
|
|
|
|
Data: dest.To4(),
|
|
|
|
|
},
|
2018-06-14 22:25:39 +02:00
|
|
|
|
)
|
|
|
|
|
if dportMin == dportMax {
|
|
|
|
|
ex = append(ex,
|
|
|
|
|
// [ immediate reg 2 0x0000f00f ]
|
|
|
|
|
&expr.Immediate{
|
|
|
|
|
Register: 2,
|
|
|
|
|
Data: binaryutil.BigEndian.PutUint16(dportMin),
|
|
|
|
|
},
|
|
|
|
|
// [ nat dnat ip addr_min reg 1 addr_max reg 0 proto_min reg 2 proto_max reg 0 ]
|
|
|
|
|
&expr.NAT{
|
|
|
|
|
Type: expr.NATTypeDestNAT,
|
|
|
|
|
Family: unix.NFPROTO_IPV4,
|
|
|
|
|
RegAddrMin: 1,
|
|
|
|
|
RegProtoMin: 2,
|
|
|
|
|
},
|
|
|
|
|
)
|
|
|
|
|
} else {
|
|
|
|
|
ex = append(ex,
|
|
|
|
|
// [ immediate reg 2 0x0000e60f ]
|
|
|
|
|
&expr.Immediate{
|
|
|
|
|
Register: 2,
|
|
|
|
|
Data: binaryutil.BigEndian.PutUint16(dportMin),
|
|
|
|
|
},
|
|
|
|
|
// [ immediate reg 3 0x0000fa0f ]
|
|
|
|
|
&expr.Immediate{
|
|
|
|
|
Register: 3,
|
|
|
|
|
Data: binaryutil.BigEndian.PutUint16(dportMax),
|
|
|
|
|
},
|
|
|
|
|
// [ nat dnat ip addr_min reg 1 addr_max reg 0 proto_min reg 2 proto_max reg 3 ]
|
|
|
|
|
&expr.NAT{
|
|
|
|
|
Type: expr.NATTypeDestNAT,
|
|
|
|
|
Family: unix.NFPROTO_IPV4,
|
|
|
|
|
RegAddrMin: 1,
|
|
|
|
|
RegProtoMin: 2,
|
|
|
|
|
RegProtoMax: 3,
|
|
|
|
|
},
|
|
|
|
|
)
|
2018-06-05 08:51:51 +02:00
|
|
|
|
}
|
2018-06-14 22:25:39 +02:00
|
|
|
|
return ex
|
2018-06-05 08:51:51 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type portForwarding struct {
|
2018-06-14 21:05:43 +02:00
|
|
|
|
Proto string `json:"proto"` // e.g. “tcp” (or “tcp,udp”)
|
2018-06-14 22:25:39 +02:00
|
|
|
|
Port string `json:"port"` // e.g. “8080” (or “8080-8090”)
|
2018-06-14 21:05:43 +02:00
|
|
|
|
DestAddr string `json:"dest_addr"` // e.g. “192.168.42.2”
|
2018-06-14 22:25:39 +02:00
|
|
|
|
DestPort string `json:"dest_port"` // e.g. “80” (or “80-90”)
|
2018-06-05 08:51:51 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type portForwardings struct {
|
|
|
|
|
Forwardings []portForwarding `json:"forwardings"`
|
|
|
|
|
}
|
|
|
|
|
|
2018-06-14 22:25:39 +02:00
|
|
|
|
var rangeRe = regexp.MustCompile(`^([0-9]+)(?:-([0-9]+))?$`)
|
|
|
|
|
|
|
|
|
|
func parsePort(p string) (min uint16, max uint16, _ error) {
|
|
|
|
|
matches := rangeRe.FindStringSubmatch(p)
|
|
|
|
|
if len(matches) == 0 {
|
|
|
|
|
return 0, 0, fmt.Errorf("malformed port %q, expected port number (e.g. 8080) or port range (e.g. 8080-8090)", p)
|
|
|
|
|
}
|
|
|
|
|
min64, err := strconv.ParseUint(matches[1], 0, 16)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return 0, 0, fmt.Errorf("ParseInt(%q): %v", matches[1], err)
|
|
|
|
|
}
|
|
|
|
|
max64 := min64
|
|
|
|
|
if matches[2] != "" {
|
|
|
|
|
max64, err = strconv.ParseUint(matches[2], 0, 16)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return 0, 0, fmt.Errorf("ParseInt(%q): %v", matches[2], err)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return uint16(min64), uint16(max64), nil
|
|
|
|
|
}
|
|
|
|
|
|
2020-02-15 23:37:29 +01:00
|
|
|
|
func applyPortForwardings(dir, ifname string, c *nftables.Conn, nat *nftables.Table, prerouting *nftables.Chain) error {
|
2018-06-05 08:51:51 +02:00
|
|
|
|
b, err := ioutil.ReadFile(filepath.Join(dir, "portforwardings.json"))
|
|
|
|
|
if err != nil {
|
|
|
|
|
if os.IsNotExist(err) {
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
var cfg portForwardings
|
|
|
|
|
if err := json.Unmarshal(b, &cfg); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for _, fw := range cfg.Forwardings {
|
2018-06-14 21:05:43 +02:00
|
|
|
|
for _, proto := range strings.Split(fw.Proto, ",") {
|
|
|
|
|
var p uint8
|
|
|
|
|
switch proto {
|
|
|
|
|
case "", "tcp":
|
|
|
|
|
p = unix.IPPROTO_TCP
|
|
|
|
|
case "udp":
|
|
|
|
|
p = unix.IPPROTO_UDP
|
|
|
|
|
default:
|
|
|
|
|
return fmt.Errorf(`unknown proto %q, expected "tcp" or "udp"`, proto)
|
|
|
|
|
}
|
2018-06-14 22:25:39 +02:00
|
|
|
|
|
|
|
|
|
min, max, err := parsePort(fw.Port)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
dmin, dmax, err := parsePort(fw.DestPort)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
2018-06-14 21:05:43 +02:00
|
|
|
|
c.AddRule(&nftables.Rule{
|
|
|
|
|
Table: nat,
|
|
|
|
|
Chain: prerouting,
|
2020-02-15 23:37:29 +01:00
|
|
|
|
Exprs: portForwardExpr(ifname, p, min, max, net.ParseIP(fw.DestAddr), dmin, dmax),
|
2018-06-14 21:05:43 +02:00
|
|
|
|
})
|
|
|
|
|
}
|
2018-06-05 08:51:51 +02:00
|
|
|
|
}
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
2018-08-08 23:15:21 +02:00
|
|
|
|
// DefaultCounterObj is overridden while testing
|
|
|
|
|
var DefaultCounterObj = &nftables.CounterObj{}
|
2018-06-23 19:56:34 +02:00
|
|
|
|
|
2018-08-08 23:15:21 +02:00
|
|
|
|
func getCounterObj(c *nftables.Conn, o *nftables.CounterObj) *nftables.CounterObj {
|
2022-03-08 22:47:18 +01:00
|
|
|
|
obj, err := c.GetObject(o)
|
2018-06-23 19:56:34 +02:00
|
|
|
|
if err != nil {
|
2018-08-08 23:15:21 +02:00
|
|
|
|
o.Bytes = DefaultCounterObj.Bytes
|
|
|
|
|
o.Packets = DefaultCounterObj.Packets
|
|
|
|
|
return o
|
2018-06-23 19:56:34 +02:00
|
|
|
|
}
|
2022-03-08 22:47:18 +01:00
|
|
|
|
if co, ok := obj.(*nftables.CounterObj); ok {
|
2018-08-08 23:15:21 +02:00
|
|
|
|
return co
|
2018-06-23 19:56:34 +02:00
|
|
|
|
}
|
2018-08-08 23:15:21 +02:00
|
|
|
|
o.Bytes = DefaultCounterObj.Bytes
|
|
|
|
|
o.Packets = DefaultCounterObj.Packets
|
|
|
|
|
return o
|
2018-06-23 19:56:34 +02:00
|
|
|
|
}
|
|
|
|
|
|
2022-03-08 09:32:09 +01:00
|
|
|
|
func hairpinDNAT() []expr.Any {
|
|
|
|
|
return []expr.Any{
|
|
|
|
|
// [ meta load oifname => reg 1 ]
|
|
|
|
|
&expr.Meta{Key: expr.MetaKeyIIFNAME, Register: 1},
|
|
|
|
|
// [ cmp eq reg 1 0x306e616c 0x00000000 0x00000000 0x00000000 ]
|
|
|
|
|
&expr.Cmp{
|
|
|
|
|
Op: expr.CmpOpEq,
|
|
|
|
|
Register: 1,
|
|
|
|
|
Data: nfifname("lan0"),
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
// [ meta load oifname => reg 1 ]
|
|
|
|
|
&expr.Meta{Key: expr.MetaKeyOIFNAME, Register: 1},
|
|
|
|
|
// [ cmp eq reg 1 0x306e616c 0x00000000 0x00000000 0x00000000 ]
|
|
|
|
|
&expr.Cmp{
|
|
|
|
|
Op: expr.CmpOpEq,
|
|
|
|
|
Register: 1,
|
|
|
|
|
Data: nfifname("lan0"),
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
// [ ct load status => reg 1 ]
|
|
|
|
|
&expr.Ct{
|
|
|
|
|
Register: 1,
|
|
|
|
|
SourceRegister: false,
|
|
|
|
|
Key: expr.CtKeySTATUS,
|
|
|
|
|
},
|
|
|
|
|
// [ bitwise reg 1 = (reg=1 & 0x00000020 ) ^ 0x00000000 ]
|
|
|
|
|
&expr.Bitwise{
|
|
|
|
|
DestRegister: 1,
|
|
|
|
|
SourceRegister: 1,
|
|
|
|
|
Len: 4,
|
|
|
|
|
Mask: []byte{0x20, 0x00, 0x00, 0x00},
|
|
|
|
|
Xor: []byte{0x00, 0x00, 0x00, 0x00},
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
// [ cmp neq reg 1 0x00000000 ]
|
|
|
|
|
&expr.Cmp{
|
|
|
|
|
Op: expr.CmpOpNeq,
|
|
|
|
|
Register: 1,
|
|
|
|
|
Data: []byte{0x00, 0x00, 0x00, 0x00},
|
|
|
|
|
},
|
|
|
|
|
// [ masq ]
|
|
|
|
|
&expr.Masq{},
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-05-09 09:55:45 +02:00
|
|
|
|
const pfChain = "router7-portforwardings"
|
|
|
|
|
|
|
|
|
|
// Only update port forwarding if there are existing rules.
|
|
|
|
|
// This is required to not stomp over podman port forwarding, for example.
|
|
|
|
|
func updatePortforwardingsOnly(dir, ifname string) error {
|
|
|
|
|
c := &nftables.Conn{}
|
|
|
|
|
|
|
|
|
|
nat, err := c.ListTable("nat")
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
chain, err := c.ListChain(nat, pfChain)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
log.Printf("rules already configured, only updating port forwardings")
|
|
|
|
|
|
|
|
|
|
c.FlushChain(chain)
|
|
|
|
|
if err := applyPortForwardings(dir, ifname, c, nat, chain); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return c.Flush()
|
|
|
|
|
}
|
|
|
|
|
|
2020-02-15 23:37:29 +01:00
|
|
|
|
func applyFirewall(dir, ifname string) error {
|
2018-06-04 10:01:09 +02:00
|
|
|
|
c := &nftables.Conn{}
|
|
|
|
|
|
2024-05-09 09:55:45 +02:00
|
|
|
|
if err := updatePortforwardingsOnly(dir, ifname); err != nil {
|
|
|
|
|
log.Printf("could not update port forwardings (%v), creating ruleset from scratch", err)
|
|
|
|
|
} else {
|
|
|
|
|
return nil // keep existing ruleset
|
|
|
|
|
}
|
|
|
|
|
|
2018-06-14 08:27:08 +02:00
|
|
|
|
c.FlushRuleset()
|
2018-06-04 10:01:09 +02:00
|
|
|
|
|
|
|
|
|
nat := c.AddTable(&nftables.Table{
|
|
|
|
|
Family: nftables.TableFamilyIPv4,
|
2024-01-20 11:41:04 -08:00
|
|
|
|
Name: "nat-gokrazy",
|
2018-06-04 10:01:09 +02:00
|
|
|
|
})
|
|
|
|
|
|
2024-05-09 09:55:45 +02:00
|
|
|
|
pf := c.AddChain(&nftables.Chain{
|
|
|
|
|
Name: pfChain,
|
|
|
|
|
Table: nat,
|
|
|
|
|
Type: nftables.ChainTypeNAT,
|
|
|
|
|
})
|
|
|
|
|
|
2018-06-05 08:51:51 +02:00
|
|
|
|
prerouting := c.AddChain(&nftables.Chain{
|
2018-06-04 10:01:09 +02:00
|
|
|
|
Name: "prerouting",
|
|
|
|
|
Hooknum: nftables.ChainHookPrerouting,
|
|
|
|
|
Priority: nftables.ChainPriorityFilter,
|
|
|
|
|
Table: nat,
|
|
|
|
|
Type: nftables.ChainTypeNAT,
|
|
|
|
|
})
|
|
|
|
|
|
2024-05-09 09:55:45 +02:00
|
|
|
|
c.AddRule(&nftables.Rule{
|
|
|
|
|
Table: nat,
|
|
|
|
|
Chain: prerouting,
|
|
|
|
|
Exprs: []expr.Any{
|
|
|
|
|
&expr.Verdict{
|
|
|
|
|
Kind: expr.VerdictJump,
|
|
|
|
|
Chain: pfChain,
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
})
|
|
|
|
|
|
2018-06-04 10:01:09 +02:00
|
|
|
|
postrouting := c.AddChain(&nftables.Chain{
|
|
|
|
|
Name: "postrouting",
|
|
|
|
|
Hooknum: nftables.ChainHookPostrouting,
|
|
|
|
|
Priority: nftables.ChainPriorityNATSource,
|
|
|
|
|
Table: nat,
|
|
|
|
|
Type: nftables.ChainTypeNAT,
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
c.AddRule(&nftables.Rule{
|
|
|
|
|
Table: nat,
|
|
|
|
|
Chain: postrouting,
|
|
|
|
|
Exprs: []expr.Any{
|
2018-06-05 08:48:30 +02:00
|
|
|
|
// meta load oifname => reg 1
|
|
|
|
|
&expr.Meta{Key: expr.MetaKeyOIFNAME, Register: 1},
|
|
|
|
|
// cmp eq reg 1 0x696c7075 0x00306b6e 0x00000000 0x00000000
|
2018-06-04 10:01:09 +02:00
|
|
|
|
&expr.Cmp{
|
|
|
|
|
Op: expr.CmpOpEq,
|
|
|
|
|
Register: 1,
|
2020-02-15 23:37:29 +01:00
|
|
|
|
Data: nfifname(ifname),
|
2018-06-04 10:01:09 +02:00
|
|
|
|
},
|
|
|
|
|
// masq
|
|
|
|
|
&expr.Masq{},
|
|
|
|
|
},
|
|
|
|
|
})
|
2018-05-27 17:30:42 +02:00
|
|
|
|
|
2022-03-08 09:32:09 +01:00
|
|
|
|
c.AddRule(&nftables.Rule{
|
|
|
|
|
Table: nat,
|
|
|
|
|
Chain: postrouting,
|
|
|
|
|
Exprs: hairpinDNAT(),
|
|
|
|
|
})
|
|
|
|
|
|
2024-05-09 09:55:45 +02:00
|
|
|
|
if err := applyPortForwardings(dir, ifname, c, nat, pf); err != nil {
|
2018-06-05 08:51:51 +02:00
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
2018-06-23 19:56:34 +02:00
|
|
|
|
filter4 := c.AddTable(&nftables.Table{
|
|
|
|
|
Family: nftables.TableFamilyIPv4,
|
2024-01-20 11:41:04 -08:00
|
|
|
|
Name: "filter-gokrazy",
|
2018-06-23 19:56:34 +02:00
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
filter6 := c.AddTable(&nftables.Table{
|
|
|
|
|
Family: nftables.TableFamilyIPv6,
|
2024-01-20 11:41:04 -08:00
|
|
|
|
Name: "filter-gokrazy",
|
2018-06-23 19:56:34 +02:00
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
for _, filter := range []*nftables.Table{filter4, filter6} {
|
|
|
|
|
forward := c.AddChain(&nftables.Chain{
|
|
|
|
|
Name: "forward",
|
|
|
|
|
Hooknum: nftables.ChainHookForward,
|
|
|
|
|
Priority: nftables.ChainPriorityFilter,
|
|
|
|
|
Table: filter,
|
|
|
|
|
Type: nftables.ChainTypeFilter,
|
|
|
|
|
})
|
|
|
|
|
|
2018-10-22 18:42:09 +02:00
|
|
|
|
c.AddRule(&nftables.Rule{
|
|
|
|
|
Table: filter,
|
|
|
|
|
Chain: forward,
|
|
|
|
|
Exprs: []expr.Any{
|
|
|
|
|
// [ meta load oifname => reg 1 ]
|
|
|
|
|
&expr.Meta{Key: expr.MetaKeyOIFNAME, Register: 1},
|
|
|
|
|
// [ cmp eq reg 1 0x30707070 0x00000000 0x00000000 0x00000000 ]
|
|
|
|
|
&expr.Cmp{
|
|
|
|
|
Op: expr.CmpOpEq,
|
|
|
|
|
Register: 1,
|
2020-02-15 23:37:29 +01:00
|
|
|
|
Data: nfifname(ifname),
|
2018-10-22 18:42:09 +02:00
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
// [ meta load l4proto => reg 1 ]
|
|
|
|
|
&expr.Meta{Key: expr.MetaKeyL4PROTO, Register: 1},
|
|
|
|
|
// [ cmp eq reg 1 0x00000006 ]
|
|
|
|
|
&expr.Cmp{
|
|
|
|
|
Op: expr.CmpOpEq,
|
|
|
|
|
Register: 1,
|
|
|
|
|
Data: []byte{unix.IPPROTO_TCP},
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
// [ payload load 1b @ transport header + 13 => reg 1 ]
|
|
|
|
|
&expr.Payload{
|
|
|
|
|
DestRegister: 1,
|
|
|
|
|
Base: expr.PayloadBaseTransportHeader,
|
|
|
|
|
Offset: 13, // TODO
|
|
|
|
|
Len: 1, // TODO
|
|
|
|
|
},
|
|
|
|
|
// [ bitwise reg 1 = (reg=1 & 0x00000002 ) ^ 0x00000000 ]
|
|
|
|
|
&expr.Bitwise{
|
|
|
|
|
DestRegister: 1,
|
|
|
|
|
SourceRegister: 1,
|
|
|
|
|
Len: 1,
|
|
|
|
|
Mask: []byte{0x02},
|
|
|
|
|
Xor: []byte{0x00},
|
|
|
|
|
},
|
|
|
|
|
// [ cmp neq reg 1 0x00000000 ]
|
|
|
|
|
&expr.Cmp{
|
|
|
|
|
Op: expr.CmpOpNeq,
|
|
|
|
|
Register: 1,
|
|
|
|
|
Data: []byte{0x00},
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
// [ rt load tcpmss => reg 1 ]
|
|
|
|
|
&expr.Rt{
|
|
|
|
|
Register: 1,
|
|
|
|
|
Key: expr.RtTCPMSS,
|
|
|
|
|
},
|
|
|
|
|
// [ byteorder reg 1 = hton(reg 1, 2, 2) ]
|
|
|
|
|
&expr.Byteorder{
|
|
|
|
|
DestRegister: 1,
|
|
|
|
|
SourceRegister: 1,
|
|
|
|
|
Op: expr.ByteorderHton,
|
|
|
|
|
Len: 2,
|
|
|
|
|
Size: 2,
|
|
|
|
|
},
|
|
|
|
|
// [ exthdr write tcpopt reg 1 => 2b @ 2 + 2 ]
|
|
|
|
|
&expr.Exthdr{
|
|
|
|
|
SourceRegister: 1,
|
|
|
|
|
Type: 2, // TODO
|
|
|
|
|
Offset: 2,
|
|
|
|
|
Len: 2,
|
|
|
|
|
Op: expr.ExthdrOpTcpopt,
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
})
|
|
|
|
|
|
2018-08-08 23:15:21 +02:00
|
|
|
|
counterObj := getCounterObj(c, &nftables.CounterObj{
|
|
|
|
|
Table: filter,
|
|
|
|
|
Name: "fwded",
|
|
|
|
|
})
|
|
|
|
|
counter := c.AddObj(counterObj).(*nftables.CounterObj)
|
2018-06-23 19:56:34 +02:00
|
|
|
|
|
2018-08-08 23:15:21 +02:00
|
|
|
|
const NFT_OBJECT_COUNTER = 1 // TODO: get into x/sys/unix
|
2018-06-23 19:56:34 +02:00
|
|
|
|
c.AddRule(&nftables.Rule{
|
|
|
|
|
Table: filter,
|
|
|
|
|
Chain: forward,
|
|
|
|
|
Exprs: []expr.Any{
|
2018-08-08 23:15:21 +02:00
|
|
|
|
// [ counter name fwded ]
|
|
|
|
|
&expr.Objref{
|
|
|
|
|
Type: NFT_OBJECT_COUNTER,
|
|
|
|
|
Name: counter.Name,
|
|
|
|
|
},
|
2018-06-23 19:56:34 +02:00
|
|
|
|
},
|
|
|
|
|
})
|
2022-03-08 22:47:18 +01:00
|
|
|
|
|
|
|
|
|
input := c.AddChain(&nftables.Chain{
|
|
|
|
|
Name: "input",
|
|
|
|
|
Hooknum: nftables.ChainHookInput,
|
|
|
|
|
Priority: nftables.ChainPriorityFilter,
|
|
|
|
|
Table: filter,
|
|
|
|
|
Type: nftables.ChainTypeFilter,
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
counterObj = getCounterObj(c, &nftables.CounterObj{
|
|
|
|
|
Table: filter,
|
|
|
|
|
Name: "inputc",
|
|
|
|
|
})
|
|
|
|
|
counter = c.AddObj(counterObj).(*nftables.CounterObj)
|
|
|
|
|
c.AddRule(&nftables.Rule{
|
|
|
|
|
Table: filter,
|
|
|
|
|
Chain: input,
|
|
|
|
|
Exprs: []expr.Any{
|
|
|
|
|
// [ counter name input ]
|
|
|
|
|
&expr.Objref{
|
|
|
|
|
Type: NFT_OBJECT_COUNTER,
|
|
|
|
|
Name: counter.Name,
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
output := c.AddChain(&nftables.Chain{
|
|
|
|
|
Name: "output",
|
|
|
|
|
Hooknum: nftables.ChainHookOutput,
|
|
|
|
|
Priority: nftables.ChainPriorityFilter,
|
|
|
|
|
Table: filter,
|
|
|
|
|
Type: nftables.ChainTypeFilter,
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
counterObj = getCounterObj(c, &nftables.CounterObj{
|
|
|
|
|
Table: filter,
|
|
|
|
|
Name: "outputc",
|
|
|
|
|
})
|
|
|
|
|
counter = c.AddObj(counterObj).(*nftables.CounterObj)
|
|
|
|
|
c.AddRule(&nftables.Rule{
|
|
|
|
|
Table: filter,
|
|
|
|
|
Chain: output,
|
|
|
|
|
Exprs: []expr.Any{
|
|
|
|
|
// [ counter name output ]
|
|
|
|
|
&expr.Objref{
|
|
|
|
|
Type: NFT_OBJECT_COUNTER,
|
|
|
|
|
Name: counter.Name,
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
})
|
2018-06-23 19:56:34 +02:00
|
|
|
|
}
|
|
|
|
|
|
2018-06-04 10:01:09 +02:00
|
|
|
|
return c.Flush()
|
2018-05-27 17:30:42 +02:00
|
|
|
|
}
|
|
|
|
|
|
2020-02-15 23:37:29 +01:00
|
|
|
|
func uplinkInterface() (string, error) {
|
|
|
|
|
names := []string{
|
|
|
|
|
"uplink0", // router7
|
|
|
|
|
"eth0", // gokrazy
|
|
|
|
|
"ens3", // distri
|
|
|
|
|
}
|
|
|
|
|
for _, ifname := range names {
|
|
|
|
|
if _, err := net.InterfaceByName(ifname); err != nil {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
return ifname, nil
|
|
|
|
|
}
|
|
|
|
|
return "", fmt.Errorf("no uplink ethernet interface found (checked %v)", names)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func applySysctl(ifname string) error {
|
|
|
|
|
sysctls := []string{
|
2018-12-25 14:08:14 +01:00
|
|
|
|
"net.ipv4.ip_forward=1",
|
|
|
|
|
"net.ipv6.conf.all.forwarding=1",
|
2022-06-21 18:30:53 +02:00
|
|
|
|
"net.ipv4.icmp_ratelimit=0",
|
|
|
|
|
"net.ipv6.icmp.ratelimit=0",
|
2020-02-15 23:37:29 +01:00
|
|
|
|
}
|
|
|
|
|
if ifname != "" {
|
|
|
|
|
sysctls = append(sysctls, "net.ipv6.conf."+ifname+".accept_ra=2")
|
|
|
|
|
}
|
|
|
|
|
for _, ctl := range sysctls {
|
2018-12-25 14:08:14 +01:00
|
|
|
|
idx := strings.Index(ctl, "=")
|
|
|
|
|
key, val := ctl[:idx], ctl[idx+1:]
|
|
|
|
|
fn := strings.Replace(key, ".", "/", -1)
|
|
|
|
|
if err := ioutil.WriteFile("/proc/sys/"+fn, []byte(val), 0644); err != nil {
|
|
|
|
|
return fmt.Errorf("sysctl(%v=%v): %v", key, val, err)
|
|
|
|
|
}
|
2018-05-27 17:30:42 +02:00
|
|
|
|
}
|
2018-05-28 09:53:54 +02:00
|
|
|
|
|
2018-05-27 17:30:42 +02:00
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
2020-06-12 17:53:13 -07:00
|
|
|
|
func Apply(dir, root string, firewall bool) error {
|
2022-06-07 23:22:08 +02:00
|
|
|
|
var cfg InterfaceConfig
|
|
|
|
|
b, err := ioutil.ReadFile(filepath.Join(dir, "interfaces.json"))
|
|
|
|
|
if err != nil && !os.IsNotExist(err) {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
if err == nil || os.IsNotExist(err) {
|
|
|
|
|
if err := json.Unmarshal(b, &cfg); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
2018-05-28 09:53:54 +02:00
|
|
|
|
|
2022-06-07 23:22:08 +02:00
|
|
|
|
// TODO: split apply into two parts: delay the up until later
|
|
|
|
|
if err := applyInterfaces(dir, root, cfg); err != nil {
|
|
|
|
|
return fmt.Errorf("interfaces: %v", err)
|
|
|
|
|
}
|
2018-05-27 17:30:42 +02:00
|
|
|
|
}
|
|
|
|
|
|
2020-02-15 23:38:37 +01:00
|
|
|
|
var errors []error
|
|
|
|
|
appendError := func(err error) {
|
|
|
|
|
errors = append(errors, err)
|
|
|
|
|
log.Println(err)
|
|
|
|
|
}
|
2018-06-02 17:36:36 +02:00
|
|
|
|
|
2022-06-07 23:22:08 +02:00
|
|
|
|
if err := applyDhcp4(dir, cfg); err != nil {
|
2020-02-15 23:38:37 +01:00
|
|
|
|
appendError(fmt.Errorf("dhcp4: %v", err))
|
2018-05-27 17:30:42 +02:00
|
|
|
|
}
|
|
|
|
|
|
2018-06-03 20:35:41 +02:00
|
|
|
|
if err := applyDhcp6(dir); err != nil {
|
2020-02-15 23:38:37 +01:00
|
|
|
|
appendError(fmt.Errorf("dhcp6: %v", err))
|
2018-05-27 17:30:42 +02:00
|
|
|
|
}
|
|
|
|
|
|
2018-06-18 08:04:56 +02:00
|
|
|
|
for _, process := range []string{
|
2018-06-27 19:44:39 +02:00
|
|
|
|
"dyndns", // depends on the public IPv4 address
|
|
|
|
|
"dnsd", // listens on private IPv4/IPv6
|
|
|
|
|
"diagd", // listens on private IPv4/IPv6
|
|
|
|
|
"backupd", // listens on private IPv4/IPv6
|
|
|
|
|
"captured", // listens on private IPv4/IPv6
|
2018-06-18 08:04:56 +02:00
|
|
|
|
} {
|
2020-06-12 17:53:13 -07:00
|
|
|
|
if err := notify.Process(path.Join(CmdRoot, process), syscall.SIGUSR1); err != nil {
|
2018-06-18 08:04:56 +02:00
|
|
|
|
log.Printf("notifying %s: %v", process, err)
|
|
|
|
|
}
|
2018-06-17 20:44:25 +02:00
|
|
|
|
}
|
|
|
|
|
|
2020-02-15 23:37:29 +01:00
|
|
|
|
ifname, err := uplinkInterface()
|
|
|
|
|
if err != nil {
|
|
|
|
|
log.Printf("uplinkInterface: %v", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if err := applySysctl(ifname); err != nil {
|
2020-02-15 23:38:37 +01:00
|
|
|
|
appendError(fmt.Errorf("sysctl: %v", err))
|
2018-05-27 17:30:42 +02:00
|
|
|
|
}
|
|
|
|
|
|
2020-06-12 17:53:13 -07:00
|
|
|
|
if firewall {
|
|
|
|
|
if err := applyFirewall(dir, ifname); err != nil {
|
|
|
|
|
appendError(fmt.Errorf("firewall: %v", err))
|
|
|
|
|
}
|
2024-01-20 11:41:04 -08:00
|
|
|
|
} else {
|
|
|
|
|
if _, err := os.Stat("/user/nft"); err == nil {
|
|
|
|
|
log.Println("Applying custom firewall")
|
|
|
|
|
cmd := &exec.Cmd{
|
|
|
|
|
Path: "/user/nft",
|
|
|
|
|
Args: []string{"/user/nft", "-f/etc/firewall.nft"},
|
|
|
|
|
Env: os.Environ(),
|
|
|
|
|
Stdout: os.Stdout,
|
|
|
|
|
Stderr: os.Stderr,
|
|
|
|
|
}
|
|
|
|
|
if err := cmd.Run(); err != nil {
|
|
|
|
|
appendError(fmt.Errorf("firewall: nft: %v", err))
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
log.Println("Firewall Disabled")
|
|
|
|
|
}
|
2018-05-27 17:30:42 +02:00
|
|
|
|
}
|
|
|
|
|
|
2018-11-26 18:29:03 +01:00
|
|
|
|
if err := applyWireGuard(dir); err != nil {
|
2020-02-15 23:38:37 +01:00
|
|
|
|
appendError(fmt.Errorf("wireguard: %v", err))
|
2018-11-26 18:29:03 +01:00
|
|
|
|
}
|
|
|
|
|
|
2020-02-15 23:38:37 +01:00
|
|
|
|
if len(errors) > 0 {
|
|
|
|
|
return fmt.Errorf("%v", errors)
|
|
|
|
|
}
|
|
|
|
|
return nil
|
2018-05-27 17:30:42 +02:00
|
|
|
|
}
|