22 Commits

Author SHA1 Message Date
Timmy Welch
28f5022603 Update dns for ipv6 addresses 2025-12-27 16:48:59 -08:00
Timmy Welch
15e2ab97d6 Merge remote-tracking branch 'github/master' 2025-12-27 16:45:54 -08:00
Michael Stapelberg
acdc5313ee netconfig: create bridge with future MAC address to avoid temporary
On one of my router7 installations, I’m using a bridge for uplink0
(so that I can use the built-in SFP interface, or fall back
to an external media converter in case of trouble without reconfig).

On that installation, I have observed the following bug:

  1. System boots, netconfig creates uplink0.
  2. The Linux kernel assigns a MAC address (e.g. ce:fa:ba:3c:66:84).
  3. Netconfig cannot add the interfaces to the bridge yet,
     presumably because they are not created yet.
  4. The dhcp4 program starts and reads the MAC address.
  5. Netconfig adds the interfaces to the bridge, the MAC changes.
  6. dhcp4 still has the old (incorrect) MAC address.
  7. dhcp4 never obtains a lease, the router never becomes healthy,
     you’re stuck in this state.

This will be prevented by not letting the kernel assign a MAC address,
but instead determining the future MAC address (in our case:
we just use the first configured MAC address) and creating the bridge
interface correctly configured to begin with, thereby eliminating
the race condition entirely.
2025-12-14 08:19:36 +01:00
dependabot[bot]
16933dd2ca build(deps): bump github.com/eclipse/paho.mqtt.golang (#94)
Bumps [github.com/eclipse/paho.mqtt.golang](https://github.com/eclipse/paho.mqtt.golang) from 1.4.1 to 1.5.1.
- [Release notes](https://github.com/eclipse/paho.mqtt.golang/releases)
- [Commits](https://github.com/eclipse/paho.mqtt.golang/compare/v1.4.1...v1.5.1)

---
updated-dependencies:
- dependency-name: github.com/eclipse/paho.mqtt.golang
  dependency-version: 1.5.1
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-03 08:29:53 +01:00
dependabot[bot]
5603d88134 build(deps): bump golang.org/x/crypto from 0.36.0 to 0.45.0 (#93)
Bumps [golang.org/x/crypto](https://github.com/golang/crypto) from 0.36.0 to 0.45.0.
- [Commits](https://github.com/golang/crypto/compare/v0.36.0...v0.45.0)

---
updated-dependencies:
- dependency-name: golang.org/x/crypto
  dependency-version: 0.45.0
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-11-20 07:37:23 +01:00
Michael Stapelberg
2189376204 dhcp: clone hardware addresses throughout
I still noticed DHCP requests with incorrect MAC source addresses.
Turns out there were still a number of incorrect HardwareAddr usages.
2025-10-16 11:23:58 +02:00
Michael Stapelberg
e2d4de4768 diagd: log error message when reporting unhealthy
I noticed that there seem to be transient periods of unhealthiness,
so let’s figure out which particular check is failing.
2025-09-14 11:48:36 +02:00
Michael Stapelberg
128a7e98ef diagd: export healthiness as prometheus metric (for monitoring) 2025-09-11 08:21:10 +02:00
Michael Stapelberg
0b9afc9273 dhcp4: clone HardwareAddr bytes defensively
I suspect that Go’s net package reuses its buffers and these bytes don’t remain
valid forever (perhaps only if the network interfaces of the machine change?).

At least that would explain why my DHCP client sent requests with a wrong address.
2025-08-15 08:29:33 +02:00
Michael Stapelberg
35fcfc15c8 GitHub Actions: run tests in nix-shell, not Docker
For some reason, the MAC address reported by dnsmasq in Docker on GitHub Actions
no longer matches the address expected by the test. With Nix, it works.
2025-07-20 22:57:13 +02:00
Michael Stapelberg
2e5277d689 internal/dns: retry over TCP upon truncated response
This fixes resolving login.tailscale.com as of the time of writing,
the first DNS name for which I noticed an error in 7 years of router7.
2025-07-20 18:31:45 +02:00
dependabot[bot]
52826d6011 build(deps): bump golang.org/x/net from 0.37.0 to 0.38.0 (#92)
Bumps [golang.org/x/net](https://github.com/golang/net) from 0.37.0 to 0.38.0.
- [Commits](https://github.com/golang/net/compare/v0.37.0...v0.38.0)

---
updated-dependencies:
- dependency-name: golang.org/x/net
  dependency-version: 0.38.0
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-04-17 16:27:12 +02:00
Michael Stapelberg
6e73981e36 add push script to publish self-hosted website (from docs/) 2025-03-20 08:48:57 +01:00
Michael Stapelberg
58fa19ba32 go.{mod,sum}: pull in latest x/ packages 2025-03-13 08:42:32 +01:00
Michael Stapelberg
ea317e0e7a go.{mod,sum}: pull in latest gokrazy/rsync 2025-03-13 08:42:10 +01:00
Michael Stapelberg
e9cfa01d4a dyndns: fix updating the root record of a zone
(Required for self-hosting gokrazy.org.)
2025-03-07 17:47:26 +01:00
dependabot[bot]
9f55a9f5dc build(deps): bump golang.org/x/net from 0.23.0 to 0.33.0 (#89)
Bumps [golang.org/x/net](https://github.com/golang/net) from 0.23.0 to 0.33.0.
- [Commits](https://github.com/golang/net/compare/v0.23.0...v0.33.0)

---
updated-dependencies:
- dependency-name: golang.org/x/net
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-02-24 06:54:42 +01:00
Michael Stapelberg
4ca26a5a32 GitHub Actions: update action versions 2025-02-24 06:50:25 +01:00
Michael Stapelberg
3fbc5260b3 Dockerfile: dnsutils is now bind9-dnsutils (for dig) 2025-02-24 06:47:27 +01:00
Michael Stapelberg
20840d4904 fix example: func Example() must be niladic 2025-02-24 06:42:48 +01:00
Michael Stapelberg
07b85b9624 backupd: serve /perm via rsync, too
This allows for more efficient incremental backup.
2025-02-23 22:28:01 +01:00
Michael Stapelberg
fe43422499 go.mod: bump language version to 1.24 2025-02-23 22:27:37 +01:00
17 changed files with 199 additions and 140 deletions

View File

@@ -9,27 +9,14 @@ on:
jobs: jobs:
build: build:
name: build name: test
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v4
- name: Set up Go 1.x - uses: actions/setup-go@v5
uses: actions/setup-go@v2
with: with:
# Run on the latest minor release of Go 1.18: go-version: 'stable'
go-version: ^1.18
id: go
- name: Check out code into the Go module directory
uses: actions/checkout@v2
- name: Share cache with other actions
uses: actions/cache@v2
with:
path: ~/go/pkg/mod
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
restore-keys: |
${{ runner.os }}-go-
- name: Ensure all files were formatted as per gofmt - name: Ensure all files were formatted as per gofmt
run: | run: |
@@ -39,64 +26,24 @@ jobs:
run: | run: |
go vet go vet
- name: Build - name: Build and Test
run: | run: |
go build -v ./cmd/... go build -v ./cmd/...
test:
name: test
runs-on: ubuntu-latest
steps:
- name: Set up Go 1.x
uses: actions/setup-go@v2
with:
# Run on the latest minor release of Go 1.18:
go-version: ^1.18
id: go
- name: Check out code into the Go module directory
uses: actions/checkout@v2
- name: Share cache with other actions
uses: actions/cache@v2
with:
path: ~/go/pkg/mod
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
restore-keys: |
${{ runner.os }}-go-
- name: Test
run: |
go test -v -race ./internal/... go test -v -race ./internal/...
integrationtest: integrationtest:
name: integrationtest name: integrationtest
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v4
- name: Set up Go 1.x - uses: actions/setup-go@v5
uses: actions/setup-go@v2
with: with:
# Run on the latest minor release of Go 1.18: go-version: 'stable'
go-version: ^1.18
id: go
- name: Check out code into the Go module directory - uses: cachix/install-nix-action@v31
uses: actions/checkout@v2
- name: Share cache with other actions
uses: actions/cache@v2
with: with:
path: ~/go/pkg/mod nix_path: nixpkgs=channel:nixos-25.05
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
restore-keys: |
${{ runner.os }}-go-
- name: Build Docker container with the tools our tests require - name: Run tests in nix-shell
run: | run: nix-shell --command 'make test'
docker build --pull --no-cache --rm -t=router7 -f travis/Dockerfile .
- name: Run tests in Docker container
run: |
exit=0; for pkg in $(go list ./integration/...); do go test -c $pkg && docker run --privileged --net=host -v $PWD:/usr/src:ro router7 /bin/sh -c "./$(basename $pkg).test -test.v" || exit=1; done; [ $exit = 0 ]

View File

@@ -16,6 +16,7 @@
package main package main
import ( import (
"context"
"flag" "flag"
"net" "net"
"net/http" "net/http"
@@ -24,6 +25,7 @@ import (
"syscall" "syscall"
"github.com/gokrazy/gokrazy" "github.com/gokrazy/gokrazy"
"github.com/gokrazy/rsync/rsyncd"
"github.com/rtr7/router7/internal/backup" "github.com/rtr7/router7/internal/backup"
"github.com/rtr7/router7/internal/multilisten" "github.com/rtr7/router7/internal/multilisten"
@@ -37,6 +39,51 @@ var (
perm = flag.String("perm", "/perm", "path to replace /perm") perm = flag.String("perm", "/perm", "path to replace /perm")
) )
var rsyncListeners = multilisten.NewPool()
type rsyncListener struct {
addr string
listener net.Listener
cancel context.CancelFunc
}
func (r *rsyncListener) ListenAndServe() error {
ln, err := net.Listen("tcp", r.addr)
if err != nil {
return err
}
r.listener = ln
ctx, cancel := context.WithCancel(context.Background())
r.cancel = cancel
rsyncServer, err := rsyncd.NewServer([]rsyncd.Module{
{
Name: "perm",
Path: "/perm",
},
})
if err != nil {
return err
}
go func() {
if err := rsyncServer.Serve(ctx, ln); err != nil {
log.Print(err)
}
}()
return nil
}
func (r *rsyncListener) Close() error {
if r.cancel != nil {
r.cancel()
r.cancel = nil
r.listener.Close()
r.listener = nil
}
return nil
}
func updateListeners() error { func updateListeners() error {
hosts, err := gokrazy.PrivateInterfaceAddrs() hosts, err := gokrazy.PrivateInterfaceAddrs()
if err != nil { if err != nil {
@@ -46,6 +93,11 @@ func updateListeners() error {
httpListeners.ListenAndServe(hosts, func(host string) multilisten.Listener { httpListeners.ListenAndServe(hosts, func(host string) multilisten.Listener {
return &http.Server{Addr: net.JoinHostPort(host, "8077")} return &http.Server{Addr: net.JoinHostPort(host, "8077")}
}) })
rsyncListeners.ListenAndServe(hosts, func(host string) multilisten.Listener {
return &rsyncListener{addr: net.JoinHostPort(host, "8873")} // unprivileged rsync
})
return nil return nil
} }

View File

@@ -30,6 +30,7 @@ import (
"path" "path"
"path/filepath" "path/filepath"
"strings" "strings"
"slices"
"syscall" "syscall"
"time" "time"
@@ -100,7 +101,8 @@ func logic() error {
if err != nil { if err != nil {
return err return err
} }
hwaddr := iface.HardwareAddr // Clone the hardware address as the backing array does not remain valid.
hwaddr := slices.Clone(iface.HardwareAddr)
// The interface may not have been configured by netconfigd yet and might // The interface may not have been configured by netconfigd yet and might
// still use the old hardware address. We overwrite it with the address that // still use the old hardware address. We overwrite it with the address that
// netconfigd is going to use to fix this issue without additional // netconfigd is going to use to fix this issue without additional

View File

@@ -31,6 +31,9 @@ import (
"syscall" "syscall"
"github.com/gokrazy/gokrazy" "github.com/gokrazy/gokrazy"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promauto"
"github.com/prometheus/client_golang/prometheus/promhttp"
"github.com/rtr7/router7/internal/diag" "github.com/rtr7/router7/internal/diag"
"github.com/rtr7/router7/internal/multilisten" "github.com/rtr7/router7/internal/multilisten"
@@ -140,6 +143,23 @@ func logic() error {
} }
w.Write(b) w.Write(b)
}) })
promauto.NewGaugeFunc(
prometheus.GaugeOpts{
Subsystem: "diagd",
Name: "healthy",
Help: "Whether diagd considers this machine healthy or not",
},
func() float64 {
mu.Lock()
re := mJSON.Evaluate()
mu.Unlock()
if err := firstError(re); err != "" {
log.Printf("prometheus metric reporting unhealthy: %v", err)
return 0
}
return 1
})
http.Handle("/metrics", promhttp.Handler())
if err := updateListeners(); err != nil { if err := updateListeners(); err != nil {
return err return err
} }

18
go.mod
View File

@@ -1,14 +1,13 @@
module github.com/rtr7/router7 module github.com/rtr7/router7
go 1.21 go 1.24.0
toolchain go1.22.2
require ( require (
github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883 github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883
github.com/digineo/go-ping v1.0.1 github.com/digineo/go-ping v1.0.1
github.com/eclipse/paho.mqtt.golang v1.4.1 github.com/eclipse/paho.mqtt.golang v1.5.1
github.com/gokrazy/gokrazy v0.0.0-20230812092215-346db1998f83 github.com/gokrazy/gokrazy v0.0.0-20230812092215-346db1998f83
github.com/gokrazy/rsync v0.2.5
github.com/google/go-cmp v0.6.0 github.com/google/go-cmp v0.6.0
github.com/google/gopacket v1.1.19 github.com/google/gopacket v1.1.19
github.com/google/nftables v0.2.1-0.20240422065334-aa8348f7904c github.com/google/nftables v0.2.1-0.20240422065334-aa8348f7904c
@@ -26,10 +25,10 @@ require (
github.com/rtr7/dhcp4 v0.0.0-20220302171438-18c84d089b46 github.com/rtr7/dhcp4 v0.0.0-20220302171438-18c84d089b46
github.com/vishvananda/netlink v1.2.1-beta.2 github.com/vishvananda/netlink v1.2.1-beta.2
github.com/vishvananda/netns v0.0.4 github.com/vishvananda/netns v0.0.4
golang.org/x/crypto v0.31.0 golang.org/x/crypto v0.45.0
golang.org/x/net v0.23.0 golang.org/x/net v0.47.0
golang.org/x/sync v0.7.0 golang.org/x/sync v0.17.0
golang.org/x/sys v0.28.0 golang.org/x/sys v0.38.0
golang.org/x/time v0.0.0-20220722155302-e5dcc9cfc0b9 golang.org/x/time v0.0.0-20220722155302-e5dcc9cfc0b9
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20220504211119-3d4a969bb56b golang.zx2c4.com/wireguard/wgctrl v0.0.0-20220504211119-3d4a969bb56b
) )
@@ -40,13 +39,14 @@ require (
github.com/digineo/go-logwrap v0.0.0-20181106161722-a178c58ea3f0 // indirect github.com/digineo/go-logwrap v0.0.0-20181106161722-a178c58ea3f0 // indirect
github.com/gokrazy/internal v0.0.0-20230211171410-9608422911d0 // indirect github.com/gokrazy/internal v0.0.0-20230211171410-9608422911d0 // indirect
github.com/google/renameio/v2 v2.0.0 // indirect github.com/google/renameio/v2 v2.0.0 // indirect
github.com/gorilla/websocket v1.4.2 // indirect github.com/gorilla/websocket v1.5.3 // indirect
github.com/josharian/native v1.1.0 // indirect github.com/josharian/native v1.1.0 // indirect
github.com/kenshaw/evdev v0.1.0 // indirect github.com/kenshaw/evdev v0.1.0 // indirect
github.com/mdlayher/genetlink v1.3.2 // indirect github.com/mdlayher/genetlink v1.3.2 // indirect
github.com/mdlayher/netlink v1.7.2 // indirect github.com/mdlayher/netlink v1.7.2 // indirect
github.com/mdlayher/socket v0.5.0 // indirect github.com/mdlayher/socket v0.5.0 // indirect
github.com/mdlayher/watchdog v0.0.0-20221003142519-49be0df7b3b5 // indirect github.com/mdlayher/watchdog v0.0.0-20221003142519-49be0df7b3b5 // indirect
github.com/mmcloughlin/md4 v0.1.2 // indirect
github.com/prometheus/client_model v0.6.1 // indirect github.com/prometheus/client_model v0.6.1 // indirect
github.com/prometheus/common v0.53.0 // indirect github.com/prometheus/common v0.53.0 // indirect
github.com/prometheus/procfs v0.14.0 // indirect github.com/prometheus/procfs v0.14.0 // indirect

34
go.sum
View File

@@ -11,8 +11,8 @@ github.com/digineo/go-logwrap v0.0.0-20181106161722-a178c58ea3f0 h1:OT/LKmj81wMy
github.com/digineo/go-logwrap v0.0.0-20181106161722-a178c58ea3f0/go.mod h1:DmqdumeAKGQNU5E8MN0ruT5ZGx8l/WbAsMbXCXcSEts= github.com/digineo/go-logwrap v0.0.0-20181106161722-a178c58ea3f0/go.mod h1:DmqdumeAKGQNU5E8MN0ruT5ZGx8l/WbAsMbXCXcSEts=
github.com/digineo/go-ping v1.0.1 h1:Yn9hwM0RY4j4D3gcmLvRJf0d7MrbucfUhnOeVDvcVyk= github.com/digineo/go-ping v1.0.1 h1:Yn9hwM0RY4j4D3gcmLvRJf0d7MrbucfUhnOeVDvcVyk=
github.com/digineo/go-ping v1.0.1/go.mod h1:uCbFC0VUqGNBNiev44BGSxfOrEAmC73GjpRje1l40Zo= github.com/digineo/go-ping v1.0.1/go.mod h1:uCbFC0VUqGNBNiev44BGSxfOrEAmC73GjpRje1l40Zo=
github.com/eclipse/paho.mqtt.golang v1.4.1 h1:tUSpviiL5G3P9SZZJPC4ZULZJsxQKXxfENpMvdbAXAI= github.com/eclipse/paho.mqtt.golang v1.5.1 h1:/VSOv3oDLlpqR2Epjn1Q7b2bSTplJIeV2ISgCl2W7nE=
github.com/eclipse/paho.mqtt.golang v1.4.1/go.mod h1:JGt0RsEwEX+Xa/agj90YJ9d9DH2b7upDZMK9HRbFvCA= github.com/eclipse/paho.mqtt.golang v1.5.1/go.mod h1:1/yJCneuyOoCOzKSsOTUc0AJfpsItBGWvYpBLimhArU=
github.com/fanliao/go-promise v0.0.0-20141029170127-1890db352a72/go.mod h1:PjfxuH4FZdUyfMdtBio2lsRr1AKEaVPwelzuHuh8Lqc= github.com/fanliao/go-promise v0.0.0-20141029170127-1890db352a72/go.mod h1:PjfxuH4FZdUyfMdtBio2lsRr1AKEaVPwelzuHuh8Lqc=
github.com/fatih/color v1.10.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM= github.com/fatih/color v1.10.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM=
github.com/gdamore/encoding v1.0.0/go.mod h1:alR0ol34c49FCSBLjhosxzcPHQbf2trDkoo5dl+VrEg= github.com/gdamore/encoding v1.0.0/go.mod h1:alR0ol34c49FCSBLjhosxzcPHQbf2trDkoo5dl+VrEg=
@@ -21,6 +21,8 @@ github.com/gokrazy/gokrazy v0.0.0-20230812092215-346db1998f83 h1:Y4sADvUYd/c0eqn
github.com/gokrazy/gokrazy v0.0.0-20230812092215-346db1998f83/go.mod h1:9q5Tg+q+YvRjC3VG0gfMFut46dhbhtAnvUEp4lPjc6c= github.com/gokrazy/gokrazy v0.0.0-20230812092215-346db1998f83/go.mod h1:9q5Tg+q+YvRjC3VG0gfMFut46dhbhtAnvUEp4lPjc6c=
github.com/gokrazy/internal v0.0.0-20230211171410-9608422911d0 h1:QTi0skQ/OM7he/5jEWA9k/DYgdwGAhw3hrUoiPGGZHM= github.com/gokrazy/internal v0.0.0-20230211171410-9608422911d0 h1:QTi0skQ/OM7he/5jEWA9k/DYgdwGAhw3hrUoiPGGZHM=
github.com/gokrazy/internal v0.0.0-20230211171410-9608422911d0/go.mod h1:ddHcxXZ/VVQOSAWcRBbkYY58+QOw4L145ye6phyDmRA= github.com/gokrazy/internal v0.0.0-20230211171410-9608422911d0/go.mod h1:ddHcxXZ/VVQOSAWcRBbkYY58+QOw4L145ye6phyDmRA=
github.com/gokrazy/rsync v0.2.5 h1:FOZ96QyRmQRPH/1SG5kpXUU4vj7x9aJtdiyb7eVQcII=
github.com/gokrazy/rsync v0.2.5/go.mod h1:dz2L8Dmv1fFpRlp6YuLNZ1UIqOfFqYir14J/8efsz6w=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
@@ -37,8 +39,8 @@ github.com/google/renameio v1.0.1/go.mod h1:t/HQoYBZSsWSNK35C6CO/TpPLDVWvxOHboWU
github.com/google/renameio/v2 v2.0.0 h1:UifI23ZTGY8Tt29JbYFiuyIU3eX+RNFtUwefq9qAhxg= github.com/google/renameio/v2 v2.0.0 h1:UifI23ZTGY8Tt29JbYFiuyIU3eX+RNFtUwefq9qAhxg=
github.com/google/renameio/v2 v2.0.0/go.mod h1:BtmJXm5YlszgC+TD4HOEEUFgkJP3nLxehU6hfe7jRt4= github.com/google/renameio/v2 v2.0.0/go.mod h1:BtmJXm5YlszgC+TD4HOEEUFgkJP3nLxehU6hfe7jRt4=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc= github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/hugelgupf/socketpair v0.0.0-20190730060125-05d35a94e714/go.mod h1:2Goc3h8EklBH5mspfHFxBnEoURQCGzQQH1ga9Myjvis= github.com/hugelgupf/socketpair v0.0.0-20190730060125-05d35a94e714/go.mod h1:2Goc3h8EklBH5mspfHFxBnEoURQCGzQQH1ga9Myjvis=
github.com/insomniacslk/dhcp v0.0.0-20220822114210-de18a9d48e84 h1:MJTy6H+EpXLeAn0P5WAWeLk6dJA3V0ik6S3VJfUyQuI= github.com/insomniacslk/dhcp v0.0.0-20220822114210-de18a9d48e84 h1:MJTy6H+EpXLeAn0P5WAWeLk6dJA3V0ik6S3VJfUyQuI=
github.com/insomniacslk/dhcp v0.0.0-20220822114210-de18a9d48e84/go.mod h1:h+MxyHxRg9NH3terB1nfRIUaQEcI0XOVkdR9LNBlp8E= github.com/insomniacslk/dhcp v0.0.0-20220822114210-de18a9d48e84/go.mod h1:h+MxyHxRg9NH3terB1nfRIUaQEcI0XOVkdR9LNBlp8E=
@@ -93,6 +95,8 @@ github.com/miekg/dns v1.1.50 h1:DQUfb9uc6smULcREF09Uc+/Gd46YWqJd5DbpPE9xkcA=
github.com/miekg/dns v1.1.50/go.mod h1:e3IlAVfNqAllflbibAZEWOXOQ+Ynzk/dDozDxY7XnME= github.com/miekg/dns v1.1.50/go.mod h1:e3IlAVfNqAllflbibAZEWOXOQ+Ynzk/dDozDxY7XnME=
github.com/mikioh/ipaddr v0.0.0-20190404000644-d465c8ab6721 h1:RlZweED6sbSArvlE924+mUcZuXKLBHA35U7LN621Bws= github.com/mikioh/ipaddr v0.0.0-20190404000644-d465c8ab6721 h1:RlZweED6sbSArvlE924+mUcZuXKLBHA35U7LN621Bws=
github.com/mikioh/ipaddr v0.0.0-20190404000644-d465c8ab6721/go.mod h1:Ickgr2WtCLZ2MDGd4Gr0geeCH5HybhRJbonOgQpvSxc= github.com/mikioh/ipaddr v0.0.0-20190404000644-d465c8ab6721/go.mod h1:Ickgr2WtCLZ2MDGd4Gr0geeCH5HybhRJbonOgQpvSxc=
github.com/mmcloughlin/md4 v0.1.2 h1:kGYl+iNbxhyz4u76ka9a+0TXP9KWt/LmnM0QhZwhcBo=
github.com/mmcloughlin/md4 v0.1.2/go.mod h1:AAxFX59fddW0IguqNzWlf1lazh1+rXeIt/Bj49cqDTQ=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_golang v1.19.0 h1:ygXvpU1AoN1MhdzckN+PyD9QJOSD4x7kmXYlnfbA6JU= github.com/prometheus/client_golang v1.19.0 h1:ygXvpU1AoN1MhdzckN+PyD9QJOSD4x7kmXYlnfbA6JU=
@@ -131,8 +135,8 @@ gitlab.com/golang-commonmark/puny v0.0.0-20191124015043-9f83538fa04f/go.mod h1:T
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U= golang.org/x/crypto v0.45.0 h1:jMBrvKuj23MTlT0bQEOBcAE0mjg8mK9RXFhRH6nyF3Q=
golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= golang.org/x/crypto v0.45.0/go.mod h1:XTGrrkGJve7CYK7J8PEww4aY7gM3qMCElcJQ8n8JdX4=
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
@@ -146,18 +150,17 @@ golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLL
golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20191007182048-72f939374954/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191007182048-72f939374954/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200425230154-ff2c4b7c35a0/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.23.0 h1:7EYJ93RZ9vYSZAIb2x3lnuvqO5zneoD6IvWjuhfxjTs= golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY=
golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= golang.org/x/sync v0.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug=
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190411185658-b44545bcd369/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190411185658-b44545bcd369/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -171,7 +174,6 @@ golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200217220822-9197077df867/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200217220822-9197077df867/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200728102440-3e129f6d46b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200728102440-3e129f6d46b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201009025420-dfb3f7c4e634/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201009025420-dfb3f7c4e634/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -183,11 +185,11 @@ golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210525143221-35b2ab0089ea/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210525143221-35b2ab0089ea/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA= golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc=
golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.27.0 h1:WP60Sv1nlK1T6SupCHbXzSaN0b9wUmsPoRS9b61A23Q= golang.org/x/term v0.37.0 h1:8EGAD0qCmHYZg6J17DvsMy9/wJ7/D/4pV/wfnld5lTU=
golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM= golang.org/x/term v0.37.0/go.mod h1:5pB4lxRNYYVZuTLmy8oR2BH8dflOR+IbTYFD8fi3254=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=

View File

@@ -20,6 +20,7 @@ import (
"errors" "errors"
"fmt" "fmt"
"net" "net"
"slices"
"sync" "sync"
"syscall" "syscall"
"time" "time"
@@ -104,10 +105,13 @@ func (c *Client) ObtainOrRenew() bool {
return return
} }
if c.hardwareAddr == nil && c.HWAddr != nil { if c.hardwareAddr == nil && c.HWAddr != nil {
c.hardwareAddr = c.HWAddr // Clone the hardware address as the backing array does not remain valid.
c.hardwareAddr = slices.Clone(c.HWAddr)
} }
if c.hardwareAddr == nil { if c.hardwareAddr == nil {
c.hardwareAddr = c.Interface.HardwareAddr // Defensive slices.Clone because I noticed c.hardwareAddr
// containing an unexpected MAC address (~/2025-08-15-*dhcp4-loss).
c.hardwareAddr = slices.Clone(c.Interface.HardwareAddr)
} }
if c.generateXID == nil { if c.generateXID == nil {
c.generateXID = dhcp4.XIDGenerator(c.hardwareAddr) c.generateXID = dhcp4.XIDGenerator(c.hardwareAddr)

View File

@@ -19,6 +19,7 @@ import (
"fmt" "fmt"
"log" "log"
"net" "net"
"slices"
"strconv" "strconv"
"time" "time"
@@ -115,9 +116,10 @@ func NewClient(cfg ClientConfig) (*Client, error) {
} }
} }
hardwareAddr := iface.HardwareAddr // Clone the hardware address as the backing array does not remain valid.
hardwareAddr := slices.Clone(iface.HardwareAddr)
if cfg.HardwareAddr != nil { if cfg.HardwareAddr != nil {
hardwareAddr = cfg.HardwareAddr hardwareAddr = slices.Clone(cfg.HardwareAddr)
} }
var duid *dhcpv6.Duid var duid *dhcpv6.Duid

View File

@@ -53,6 +53,7 @@ type Server struct {
Mux *dns.ServeMux Mux *dns.ServeMux
once bool once bool
client *dns.Client client *dns.Client
tcpClient *dns.Client
domain lcHostname domain lcHostname
sometimes *rate.Limiter sometimes *rate.Limiter
prom struct { prom struct {
@@ -87,6 +88,7 @@ func NewServer(addr, domain string) *Server {
server := &Server{ server := &Server{
Mux: dns.NewServeMux(), Mux: dns.NewServeMux(),
client: &dns.Client{}, client: &dns.Client{},
tcpClient: &dns.Client{Net: "tcp"},
domain: lcHostname(strings.ToLower(domain)), domain: lcHostname(strings.ToLower(domain)),
upstream: []string{ upstream: []string{
// https://developers.google.com/speed/public-dns/docs/using#google_public_dns_ip_addresses // https://developers.google.com/speed/public-dns/docs/using#google_public_dns_ip_addresses
@@ -547,6 +549,18 @@ func (s *Server) handleRequest(w dns.ResponseWriter, r *dns.Msg) {
} }
continue // fall back to next-slower upstream continue // fall back to next-slower upstream
} }
if in.Truncated {
// Truncated response (exceeds UDP packet size), retry over TCP:
// https://www.rfc-editor.org/rfc/rfc2181#section-9
in, _, err = s.tcpClient.Exchange(r, u)
if err != nil {
if s.sometimes.Allow() {
log.Printf("resolving %v failed: %v", r.Question, err)
}
continue // fall back to next-slower upstream
}
}
if len(in.Answer) > 1 { if len(in.Answer) > 1 {
if in.Answer[0].Header().Rrtype == dns.TypeCNAME { if in.Answer[0].Header().Rrtype == dns.TypeCNAME {
for i, rr := range in.Answer { for i, rr := range in.Answer {

View File

@@ -36,7 +36,11 @@ func Update(ctx context.Context, zone string, record libdns.Record, provider Rec
var updated []libdns.Record var updated []libdns.Record
for _, rec := range existing { for _, rec := range existing {
if rec.Name+"."+zone != record.Name || rec.Type != record.Type { fullName := rec.Name + "." + zone
if rec.Name == "" {
fullName = zone
}
if fullName != record.Name || rec.Type != record.Type {
continue continue
} }

View File

@@ -327,8 +327,17 @@ func LinkAddress(dir, ifname string) (net.IP, error) {
func applyBridges(cfg *InterfaceConfig) error { func applyBridges(cfg *InterfaceConfig) error {
for _, bridge := range cfg.Bridges { for _, bridge := range cfg.Bridges {
if _, err := netlink.LinkByName(bridge.Name); err != nil { if _, err := netlink.LinkByName(bridge.Name); err != nil {
log.Printf("creating bridge %s", bridge.Name) linkAttrs := netlink.LinkAttrs{Name: bridge.Name}
link := &netlink.Bridge{LinkAttrs: netlink.LinkAttrs{Name: bridge.Name}} // Set the bridge MAC address at creation time to avoid a race
// condition where dhcp4 might start before the first interface is
// added to the bridge and see a random kernel-assigned MAC.
if len(bridge.InterfaceHardwareAddrs) > 0 {
if hwaddr, err := net.ParseMAC(bridge.InterfaceHardwareAddrs[0]); err == nil {
linkAttrs.HardwareAddr = hwaddr
}
}
log.Printf("creating bridge %s (with hwaddr %v)", bridge.Name, linkAttrs.HardwareAddr)
link := &netlink.Bridge{LinkAttrs: linkAttrs}
if err := netlink.LinkAdd(link); err != nil { if err := netlink.LinkAdd(link); err != nil {
return fmt.Errorf("netlink.LinkAdd: %v", err) return fmt.Errorf("netlink.LinkAdd: %v", err)
} }

View File

@@ -41,6 +41,31 @@ func NewServer() (*Server, error) {
return &Server{}, nil return &Server{}, nil
} }
func (s *Server) UpdateDNS(b []byte) error {
m,err := ndp.ParseMessage(b)
if err != nil {
return err
}
if m.Type() != ipv6.ICMPTypeNeighborAdvertisement {
return fmt.Errorf("incorrect icmp message recieved expected %s, got %s", ipv6.ICMPTypeNeighborAdvertisement, m.Type())
}
n :=m.(*ndp.NeighborAdvertisement)
var hw net.HardwareAddr
for _,o := range n.Options {
if o.Code() == uint8(ndp.Target) {
ll := o.(*ndp.LinkLayerAddress)
hw = ll.Addr
break
}
}
if hw == nil || !n.TargetAddress.IsGlobalUnicast() || !n.Solicited {
// Ignore advertisements that donot provide the MAC, are not solicited and are not global unicast addresses
return nil
}
log.Printf("found IPv6 address %s for MAC address %s", n.TargetAddress, hw)
return nil
}
func (s *Server) SetPrefixes(prefixes []net.IPNet) { func (s *Server) SetPrefixes(prefixes []net.IPNet) {
s.mu.Lock() s.mu.Lock()
if s.ifname != "" { if s.ifname != "" {
@@ -75,6 +100,7 @@ func (s *Server) Serve(ifname string, conn net.PacketConn) error {
var filter ipv6.ICMPFilter var filter ipv6.ICMPFilter
filter.SetAll(true) filter.SetAll(true)
filter.Accept(ipv6.ICMPTypeRouterSolicitation) filter.Accept(ipv6.ICMPTypeRouterSolicitation)
filter.Accept(ipv6.ICMPTypeNeighborAdvertisement)
if err := s.pc.SetICMPFilter(&filter); err != nil { if err := s.pc.SetICMPFilter(&filter); err != nil {
return err return err
} }
@@ -101,6 +127,9 @@ func (s *Server) Serve(ifname string, conn net.PacketConn) error {
// TODO: isnt this guaranteed by the filter above? // TODO: isnt this guaranteed by the filter above?
if n == 0 || if n == 0 ||
ipv6.ICMPType(buf[0]) != ipv6.ICMPTypeRouterSolicitation { ipv6.ICMPType(buf[0]) != ipv6.ICMPTypeRouterSolicitation {
if ipv6.ICMPType(buf[0]) == ipv6.ICMPTypeNeighborAdvertisement{
s.UpdateDNS(buf)
}
continue continue
} }
if err := s.sendAdvertisement(addr); err != nil { if err := s.sendAdvertisement(addr); err != nil {

View File

@@ -23,7 +23,9 @@ import (
"github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp"
) )
func Example(t *testing.T) { var t *testing.T = nil // TODO: test not currently runnable
func Example() {
dnsmasq := dnsmasq.Run(t, "veth0b", "ns0") dnsmasq := dnsmasq.Run(t, "veth0b", "ns0")
defer dnsmasq.Kill() defer dnsmasq.Kill()
// test code here // test code here

2
push Executable file
View File

@@ -0,0 +1,2 @@
#!/bin/sh
rsync --exclude .git -av ./docs/ router7:/perm/srv/router7.org/

11
shell.nix Normal file
View File

@@ -0,0 +1,11 @@
{
pkgs ? import <nixpkgs> { },
}:
pkgs.mkShell {
packages = with pkgs; [
dnsmasq
dig
ndisc6
nftables
];
}

View File

@@ -1,18 +0,0 @@
# vim:ft=Dockerfile
FROM debian:sid
RUN echo force-unsafe-io > /etc/dpkg/dpkg.cfg.d/docker-apt-speedup
# Paper over occasional network flakiness of some mirrors.
RUN echo 'APT::Acquire::Retries "5";' > /etc/apt/apt.conf.d/80retry
# NOTE: I tried exclusively using gce_debian_mirror.storage.googleapis.com
# instead of httpredir.debian.org, but the results (Fetched 123 MB in 36s (3357
# kB/s)) are not any better than httpredir.debian.org (Fetched 123 MB in 34s
# (3608 kB/s)). Hence, lets stick with httpredir.debian.org (default) for now.
RUN apt-get update && \
DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
dnsmasq ndisc6 nftables dnsutils strace wireguard iproute2 && \
rm -rf /var/lib/apt/lists/*
WORKDIR /usr/src

View File

@@ -1,23 +0,0 @@
#!/bin/bash
loopdev=$(sudo losetup -Pf --show /tmp/router7-qemu/disk.img)
sudo mkfs.ext4 -m 1 "${loopdev}p4"
sudo mount "${loopdev}p4" /mnt
# TODO: make github.com/gokrazy/serial-busybox work with GOARCH=amd64
sudo cp ~/src/busybox-1.22.0-amd64/busybox /mnt/sh || true
cat <<'EOT' | sudo tee /mnt/interfaces.json
{
"interfaces": [
{
"hardware_addr": "52:55:00:d1:55:03",
"name": "uplink0"
},
{
"hardware_addr": "52:55:00:d1:55:04",
"name": "lan0",
"addr": "10.254.0.1/24"
}
]
}
EOT
sudo umount /mnt
sudo losetup -d "${loopdev}"