Compare commits
4 Commits
Author | SHA1 | Date | |
---|---|---|---|
1049057f69 | |||
0622e00f64 | |||
c0f98d4a25 | |||
db53b259a6 |
9
Makefile
9
Makefile
@ -6,6 +6,15 @@ PKGS := github.com/rtr7/router7/cmd/... \
|
||||
github.com/stapelberg/zkj-nas-tools/wolgw \
|
||||
github.com/gokrazy/gdns
|
||||
|
||||
build:
|
||||
mkdir -p result
|
||||
GOOS=linux go build -o ./result github.com/rtr7/router7/cmd/...
|
||||
GOOS=linux go build -o ./result/rtr7-init -ldflags "-X main.buildTimestamp=$(shell date '+%Y-%m-%dT%H:%M:%S%z') -X github.com/gokrazy/gokrazy.httpPassword=hello" init/init.go
|
||||
|
||||
clean:
|
||||
rm -rf result
|
||||
go clean -cache
|
||||
|
||||
image:
|
||||
ifndef DIR
|
||||
@echo variable DIR unset
|
||||
|
161
README.md
161
README.md
@ -8,4 +8,163 @@ router7 is a pure-Go implementation of a small home internet router. It comes wi
|
||||
|
||||
Note that this project should be considered a (working!) tech demo. Feature requests will likely not be implemented, and see [CONTRIBUTING.md](CONTRIBUTING.md) for details about which contributions are welcome.
|
||||
|
||||
**For more details, please see [router7.org](https://router7.org/)**
|
||||
## Motivation
|
||||
|
||||
Before starting router7, I was using the [Turris Omnia](https://omnia.turris.cz/en/) router running OpenWrt. That worked fine up until May 2018, when an automated update pulled in a new version of [odhcp6c](https://git.openwrt.org/?p=project/odhcp6c.git;a=shortlog), OpenWrt’s DHCPv6 client. That version is incompatible with fiber7’s DHCP server setup (I think there are shortcomings on both sides).
|
||||
|
||||
It was not only quicker to develop my own router than to wait for either side to resolve the issue, but it was also a lot of fun and allowed me to really tailor my router to my needs, experimenting with a bunch of interesting ideas I had.
|
||||
|
||||
## Project goals
|
||||
|
||||
* Maximize internet connectivity: retain the most recent DHCP configuration across reboots and even after its expiration (chances are the DHCP server will be back before the configuration stops working).
|
||||
* Unit/integration tests use fiber7 packet capture files to minimize the chance of software changes breaking my connectivity.
|
||||
* Safe and quick updates
|
||||
* Auto-rollback of updates which result in loss of connectivity: the diagnostics daemon assesses connectivity state, the update tool reads it and rolls back faulty updates.
|
||||
* Thanks to kexec, updates translate into merely 13s of internet connectivity loss.
|
||||
* Easy debugging
|
||||
* Configuration-related network packets (e.g. DHCP, IPv6 neighbor/router advertisements) are stored in a ring buffer which can be streamed into [Wireshark](https://www.wireshark.org/), allowing for live and retro-active debugging.
|
||||
* The diagnostics daemon performs common diagnostic steps (ping, traceroute, …) for you.
|
||||
* All state in the system is stored as human-readable JSON within the `/perm` partition and can be modified.
|
||||
|
||||
## Hardware
|
||||
|
||||
The reference hardware platform is the [PC Engines™ apu2c4](https://pcengines.ch/apu2c4.htm) system board. It features a 1 GHz quad core amd64 CPU, 4 GB of RAM, 3 Ethernet ports and a DB9 serial port. It conveniently supports PXE boot, the schematics and bootloader sources are available. I recommend the [msata16g](https://pcengines.ch/msata16g.htm) SSD module for reliable persistent storage and the [usbcom1a](https://pcengines.ch/usbcom1a.htm) serial adapter if you don’t have one already.
|
||||
|
||||
Other hardware might work, too, but is not tested.
|
||||
|
||||
### Teensy rebootor
|
||||
|
||||
The cheap and widely-available [Teensy++ USB development board](https://www.pjrc.com/store/teensypp.html) comes with a firmware called rebootor, which is used by the [`teensy_loader_cli`](https://www.pjrc.com/teensy/loader_cli.html) program to perform hard resets.
|
||||
|
||||
This setup can be used to programmatically reset the apu2c4 (from `rtr7-recover`) by connecting the Teensy++ to the [apu2c4’s reset pins](http://pcengines.ch/pdf/apu2.pdf):
|
||||
* connect the Teensy++’s `GND` pin to the apu2c4 J2’s pin 4 (`GND`)
|
||||
* connect the Teensy++’s `B7` pin to the apu2c4 J2’s pin 5 (`3.3V`, resets when pulled to `GND`)
|
||||
|
||||
You can find a working rebootor firmware .hex file at https://github.com/PaulStoffregen/teensy_loader_cli/issues/38
|
||||
|
||||
## Architecture
|
||||
|
||||
router7 is based on [gokrazy](https://gokrazy.org/): it is an appliance which gets packed into a hard disk image, containing a FAT partition with the kernel, a read-only SquashFS partition for the root file system and an ext4 partition for permanent data.
|
||||
|
||||
The individual services can be found in [github.com/rtr7/router7/cmd](https://godoc.org/github.com/rtr7/router7/cmd).
|
||||
|
||||
* Each service runs in a separate process.
|
||||
* Services communicate with each other by persisting state files. E.g., `cmd/dhcp4` writes `/perm/dhcp4/wire/lease.json`.
|
||||
* A service notifies other services about state changes by sending them signal `SIGUSR1`.
|
||||
|
||||
### Configuration files
|
||||
|
||||
| File | Consumer(s) | Purpose |
|
||||
|---|---|---|
|
||||
| `/perm/interfaces.json` | `netconfigd` | Set IP/MAC addresses of `uplink0` and `lan0` |
|
||||
| `/perm/portforwardings.json` | `netconfigd` | Configure nftables port forwarding rules |
|
||||
| `/perm/dhcp6/duid` | `dhcp6` | Set DHCP Unique Identifier (DUID) for obtaining static leases |
|
||||
|
||||
### State files
|
||||
|
||||
| File | Producer | Consumer(s) | Purpose |
|
||||
|---|---|---|---|
|
||||
| `/perm/dhcp4/wire/ack` | `dhcp4` | `dhcp4` | last DHCPACK packet for renewals across restarts |
|
||||
| `/perm/dhcp4/wire/lease.json` | `dhcp4` | `netconfigd` | Obtained DHCPv4 lease |
|
||||
| `/perm/dhcp6/wire/lease.json` | `dhcp6` | `netconfigd`, `radvd` | Obtained DHCPv6 lease |
|
||||
| `/perm/dhcp4d/leases.json` | `dhcp4d` | `dhcp4d`, `dnsd` | DHCPv4 leases handed out (including hostnames) |
|
||||
|
||||
### Available ports
|
||||
|
||||
| Port | Purpose |
|
||||
|---|---|
|
||||
| `<public>:8053` | `dnsd` metrics (forwarded requests)
|
||||
| `<public>:8066` | `netconfigd` metrics (nftables counters)
|
||||
| `<private>:80` | gokrazy web interface
|
||||
| `<private>:67` | `dhcp4d`
|
||||
| `<private>:58` | `radvd`
|
||||
| `<private>:53` | `dnsd`
|
||||
| `<private>:8077` | `backupd` (serve backup.tar.gz)
|
||||
| `<private>:7733` | `diagd` (perform diagnostics)
|
||||
| `<private>:5022` | `captured` (serve captured packets)
|
||||
|
||||
Here’s an example of the diagd output:
|
||||
|
||||
<img src="https://github.com/rtr7/router7/raw/master/2018-07-14-diagd.png"
|
||||
width="800" alt="diagd output">
|
||||
|
||||
Here’s an example of the metrics when scraped with [Prometheus](https://prometheus.io/) and displayed in [Grafana](https://grafana.com/):
|
||||
|
||||
<img src="https://github.com/rtr7/router7/raw/master/2018-07-14-grafana.png"
|
||||
width="800" alt="metrics in grafana">
|
||||
|
||||
## Installation
|
||||
|
||||
Connect your serial adapter ([usbcom1a](https://pcengines.ch/usbcom1a.htm) works well if you don’t have one already) to the apu2c4 and start a program to use it, e.g. `screen /dev/ttyUSB0 115200`. Then, power on the apu2c4 and configure it to do PXE boot:
|
||||
|
||||
* Press `F10` to enter the boot menu
|
||||
* Press `3` to enter setup
|
||||
* Press `n` to enable network boot
|
||||
* Press `c` to move mSATA to the top of the boot order
|
||||
* Press `e` to move iPXE to the top of the boot order
|
||||
* Press `s` to save configuration and exit
|
||||
|
||||
Connect a network cable on `net0`, the port closest to the serial console port:
|
||||
|
||||
<img src="https://github.com/rtr7/router7/raw/master/devsetup.jpg"
|
||||
width="800" alt="router7 development setup">
|
||||
|
||||
Next, build a router7 image:
|
||||
|
||||
```
|
||||
go get -u github.com/gokrazy/tools/cmd/gokr-packer github.com/rtr7/tools/cmd/...
|
||||
go get -u -d github.com/rtr7/router7
|
||||
mkdir /tmp/recovery
|
||||
GOARCH=amd64 gokr-packer \
|
||||
-hostname=router7 \
|
||||
-overwrite_boot=/tmp/recovery/boot.img \
|
||||
-overwrite_mbr=/tmp/recovery/mbr.img \
|
||||
-overwrite_root=/tmp/recovery/root.img \
|
||||
-kernel_package=github.com/rtr7/kernel \
|
||||
-firmware_package=github.com/rtr7/kernel \
|
||||
-gokrazy_pkgs=github.com/gokrazy/gokrazy/cmd/ntp \
|
||||
-serial_console=ttyS0,115200n8 \
|
||||
github.com/rtr7/router7/cmd/...
|
||||
```
|
||||
|
||||
Run `rtr7-recover -boot=/tmp/recovery/boot.img -mbr=/tmp/recovery/mbr.img -root=/tmp/recovery/root.img` to:
|
||||
|
||||
* trigger a reset if a Teensy with the rebootor firmware is attached
|
||||
* serve a DHCP lease to all clients which request PXE boot (i.e., your apu2c4)
|
||||
* serve via TFTP:
|
||||
* the PXELINUX bootloader
|
||||
* the router7 kernel
|
||||
* an initrd archive containing the rtr7-recovery-init program and mke2fs
|
||||
* serve via HTTP the boot and root images
|
||||
* optionally serve via HTTP a backup.tar.gz image containing files for /perm (e.g. for moving to new hardware, rolling back corrupted state, or recovering from a disk failure)
|
||||
* exit once the router successfully wrote the images to disk
|
||||
|
||||
### Updates
|
||||
|
||||
Run e.g. `rtr7-safe-update -updates_dir=$HOME/router7/updates` to:
|
||||
|
||||
* verify the router currently has connectivity, abort the update otherwise
|
||||
* download a backup archive of `/perm`
|
||||
* build a new image
|
||||
* update the router
|
||||
* wait until the router restored connectivity, roll back the update using `rtr7-recover` otherwise
|
||||
|
||||
The update step uses kexec to reduce the downtime to approximately 15 seconds.
|
||||
|
||||
### Manual Recovery
|
||||
|
||||
Given `rtr7-safe-update`’s safeguards, manual recovery should rarely be required.
|
||||
|
||||
To manually roll back to an older image, invoke `rtr7-safe-update` via the
|
||||
`recover.bash` script in the image directory underneath `-updates_dir`, e.g.:
|
||||
|
||||
```
|
||||
% cd ~/router7/updates/2018-07-03T17:33:52+02:00
|
||||
% ./recover.bash
|
||||
```
|
||||
|
||||
### Prometheus
|
||||
|
||||
See https://github.com/rtr7/router7/tree/master/contrib/prometheus for example
|
||||
configuration files, and install the [router7 Grafana
|
||||
Dashboard](https://grafana.com/dashboards/8288).
|
||||
|
@ -16,6 +16,7 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"net"
|
||||
"net/http"
|
||||
"os"
|
||||
@ -29,9 +30,12 @@ import (
|
||||
"github.com/rtr7/router7/internal/teelogger"
|
||||
)
|
||||
|
||||
var log = teelogger.NewConsole()
|
||||
var (
|
||||
log = teelogger.NewConsole()
|
||||
httpListeners = multilisten.NewPool()
|
||||
|
||||
var httpListeners = multilisten.NewPool()
|
||||
perm = flag.String("perm", "/perm", "path to replace /perm")
|
||||
)
|
||||
|
||||
func updateListeners() error {
|
||||
hosts, err := gokrazy.PrivateInterfaceAddrs()
|
||||
@ -47,7 +51,7 @@ func updateListeners() error {
|
||||
|
||||
func logic() error {
|
||||
http.HandleFunc("/backup.tar.gz", func(w http.ResponseWriter, r *http.Request) {
|
||||
if err := backup.Archive(w, "/perm"); err != nil {
|
||||
if err := backup.Archive(w, *perm); err != nil {
|
||||
log.Printf("backup.tar.gz: %v", err)
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
|
@ -24,6 +24,7 @@ import (
|
||||
"log"
|
||||
"os"
|
||||
"os/signal"
|
||||
"strings"
|
||||
"sync"
|
||||
"syscall"
|
||||
|
||||
@ -38,6 +39,7 @@ import (
|
||||
)
|
||||
|
||||
var (
|
||||
perm = flag.String("perm", "/perm", "path to replace /perm")
|
||||
hostKeyPath = flag.String("host_key",
|
||||
"/perm/breakglass.host_key",
|
||||
"path to a PEM-encoded RSA, DSA or ECDSA private key (create using e.g. ssh-keygen -f /perm/breakglass.host_key -N '' -t rsa)")
|
||||
@ -149,6 +151,9 @@ func logic() error {
|
||||
|
||||
func main() {
|
||||
flag.Parse()
|
||||
if *hostKeyPath == "/perm/breakglass.host_key" && *perm != "/perm" {
|
||||
*hostKeyPath = strings.Replace(*hostKeyPath, "/perm", *perm, 1)
|
||||
}
|
||||
if err := logic(); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
@ -24,7 +24,9 @@ import (
|
||||
"net"
|
||||
"os"
|
||||
"os/signal"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
@ -43,6 +45,7 @@ var log = teelogger.NewConsole()
|
||||
var (
|
||||
netInterface = flag.String("interface", "uplink0", "network interface to operate on")
|
||||
stateDir = flag.String("state_dir", "/perm/dhcp4", "directory in which to store lease data (wire/lease.json) and last ACK (wire/ack)")
|
||||
perm = flag.String("perm", "/perm", "path to replace /perm")
|
||||
)
|
||||
|
||||
func logic() error {
|
||||
@ -59,7 +62,7 @@ func logic() error {
|
||||
// still use the old hardware address. We overwrite it with the address that
|
||||
// netconfigd is going to use to fix this issue without additional
|
||||
// synchronization.
|
||||
details, err := netconfig.Interface("/perm", *netInterface)
|
||||
details, err := netconfig.Interface(*perm, *netInterface)
|
||||
if err == nil {
|
||||
if spoof := details.SpoofHardwareAddr; spoof != "" {
|
||||
if addr, err := net.ParseMAC(spoof); err == nil {
|
||||
@ -118,7 +121,7 @@ func logic() error {
|
||||
if err := renameio.WriteFile(ackFn, buf.Bytes(), 0644); err != nil {
|
||||
return fmt.Errorf("persisting DHCPACK to %s: %v", ackFn, err)
|
||||
}
|
||||
if err := notify.Process("/user/netconfigd", syscall.SIGUSR1); err != nil {
|
||||
if err := notify.Process(path.Join(path.Dir(os.Args[0]), "/netconfigd"), syscall.SIGUSR1); err != nil {
|
||||
log.Printf("notifying netconfig: %v", err)
|
||||
}
|
||||
select {
|
||||
@ -138,6 +141,9 @@ func logic() error {
|
||||
func main() {
|
||||
// TODO: drop privileges, run as separate uid?
|
||||
flag.Parse()
|
||||
if *stateDir == "/perm/dhcp4" && *perm != "/perm" {
|
||||
*stateDir = strings.Replace(*stateDir, "/perm", *perm, 1)
|
||||
}
|
||||
if err := logic(); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
@ -28,6 +28,7 @@ import (
|
||||
"net/http"
|
||||
"os"
|
||||
"os/signal"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strings"
|
||||
@ -50,14 +51,16 @@ import (
|
||||
"github.com/rtr7/router7/internal/teelogger"
|
||||
)
|
||||
|
||||
var iface = flag.String("interface", "lan0", "ethernet interface to listen for DHCPv4 requests on")
|
||||
var (
|
||||
log = teelogger.NewConsole()
|
||||
nonExpiredLeases = promauto.NewGauge(prometheus.GaugeOpts{
|
||||
Name: "non_expired_leases",
|
||||
Help: "Number of non-expired DHCP leases",
|
||||
})
|
||||
|
||||
var log = teelogger.NewConsole()
|
||||
|
||||
var nonExpiredLeases = promauto.NewGauge(prometheus.GaugeOpts{
|
||||
Name: "non_expired_leases",
|
||||
Help: "Number of non-expired DHCP leases",
|
||||
})
|
||||
iface = flag.String("interface", "lan0", "ethernet interface to listen for DHCPv4 requests on")
|
||||
perm = flag.String("perm", "/perm", "path to replace /perm")
|
||||
)
|
||||
|
||||
func updateNonExpired(leases []*dhcp4d.Lease) {
|
||||
now := time.Now()
|
||||
@ -71,7 +74,7 @@ func updateNonExpired(leases []*dhcp4d.Lease) {
|
||||
nonExpiredLeases.Set(float64(nonExpired))
|
||||
}
|
||||
|
||||
var ouiDB = oui.NewDB("/perm/dhcp4d/oui")
|
||||
var ouiDB = oui.NewDB(path.Join(*perm, "/dhcp4d/oui"))
|
||||
|
||||
var (
|
||||
leasesMu sync.Mutex
|
||||
@ -218,7 +221,7 @@ func updateListeners() error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if net1, err := multilisten.IPv6Net1("/perm"); err == nil {
|
||||
if net1, err := multilisten.IPv6Net1(*perm); err == nil {
|
||||
hosts = append(hosts, net1)
|
||||
}
|
||||
|
||||
@ -393,7 +396,7 @@ func newSrv(permDir string) (*srv, error) {
|
||||
errs <- err
|
||||
}
|
||||
updateNonExpired(leases)
|
||||
if err := notify.Process("/user/dnsd", syscall.SIGUSR1); err != nil {
|
||||
if err := notify.Process(path.Join(path.Dir(os.Args[0]), "/dnsd"), syscall.SIGUSR1); err != nil {
|
||||
log.Printf("notifying dnsd: %v", err)
|
||||
}
|
||||
}
|
||||
@ -422,7 +425,7 @@ func (s *srv) run(ctx context.Context) error {
|
||||
func main() {
|
||||
// TODO: drop privileges, run as separate uid?
|
||||
flag.Parse()
|
||||
srv, err := newSrv("/perm")
|
||||
srv, err := newSrv(*perm)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
@ -22,6 +22,7 @@ import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"os/signal"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"syscall"
|
||||
"time"
|
||||
@ -34,15 +35,17 @@ import (
|
||||
|
||||
var log = teelogger.NewConsole()
|
||||
|
||||
var perm = flag.String("perm", "/perm", "path to replace /perm")
|
||||
|
||||
func logic() error {
|
||||
const leasePath = "/perm/dhcp6/wire/lease.json"
|
||||
leasePath := path.Join(*perm, "/dhcp6/wire/lease.json")
|
||||
if err := os.MkdirAll(filepath.Dir(leasePath), 0755); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
duid, err := ioutil.ReadFile("/perm/dhcp6/duid")
|
||||
duid, err := ioutil.ReadFile(path.Join(*perm, "/dhcp6/duid"))
|
||||
if err != nil {
|
||||
log.Printf("could not read /perm/dhcp6/duid (%v), proceeding with DUID-LLT", err)
|
||||
log.Printf("could not read %s (%v), proceeding with DUID-LLT", path.Join(*perm, "/dhcp6/duid"), err)
|
||||
}
|
||||
|
||||
c, err := dhcp6.NewClient(dhcp6.ClientConfig{
|
||||
@ -68,10 +71,10 @@ func logic() error {
|
||||
if err := renameio.WriteFile(leasePath, b, 0644); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := notify.Process("/user/netconfigd", syscall.SIGUSR1); err != nil {
|
||||
if err := notify.Process(path.Join(path.Dir(os.Args[0]), "/netconfigd"), syscall.SIGUSR1); err != nil {
|
||||
log.Printf("notifying netconfig: %v", err)
|
||||
}
|
||||
if err := notify.Process("/user/radvd", syscall.SIGUSR1); err != nil {
|
||||
if err := notify.Process(path.Join(path.Dir(os.Args[0]), "/radvd"), syscall.SIGUSR1); err != nil {
|
||||
log.Printf("notifying radvd: %v", err)
|
||||
}
|
||||
select {
|
||||
|
@ -38,6 +38,8 @@ import (
|
||||
|
||||
var httpListeners = multilisten.NewPool()
|
||||
|
||||
var perm = flag.String("perm", "/perm", "path to replace /perm")
|
||||
|
||||
func updateListeners() error {
|
||||
hosts, err := gokrazy.PrivateInterfaceAddrs()
|
||||
if err != nil {
|
||||
@ -134,6 +136,7 @@ func logic() error {
|
||||
|
||||
func main() {
|
||||
flag.Parse()
|
||||
diag.Perm = *perm
|
||||
|
||||
if err := logic(); err != nil {
|
||||
log.Fatal(err)
|
||||
|
@ -24,6 +24,7 @@ import (
|
||||
"net/http"
|
||||
"os"
|
||||
"os/signal"
|
||||
"path"
|
||||
"syscall"
|
||||
|
||||
"github.com/gokrazy/gokrazy"
|
||||
@ -40,6 +41,9 @@ import (
|
||||
var (
|
||||
httpListeners = multilisten.NewPool()
|
||||
dnsListeners = multilisten.NewPool()
|
||||
|
||||
perm = flag.String("perm", "/perm", "path to replace /perm")
|
||||
domain = flag.String("domain", "lan", "domain name for your network")
|
||||
)
|
||||
|
||||
func updateListeners(mux *miekgdns.ServeMux) error {
|
||||
@ -56,7 +60,7 @@ func updateListeners(mux *miekgdns.ServeMux) error {
|
||||
}}
|
||||
})
|
||||
|
||||
if net1, err := multilisten.IPv6Net1("/perm"); err == nil {
|
||||
if net1, err := multilisten.IPv6Net1(*perm); err == nil {
|
||||
hosts = append(hosts, net1)
|
||||
}
|
||||
|
||||
@ -75,13 +79,13 @@ func (a *listenerAdapter) Close() error { return a.Shutdown() }
|
||||
|
||||
func logic() error {
|
||||
// TODO: set correct upstream DNS resolver(s)
|
||||
ip, err := netconfig.LinkAddress("/perm", "lan0")
|
||||
ip, err := netconfig.LinkAddress(*perm, "lan0")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
srv := dns.NewServer(ip.String()+":53", "lan")
|
||||
srv := dns.NewServer(ip.String()+":53", *domain)
|
||||
readLeases := func() error {
|
||||
b, err := ioutil.ReadFile("/perm/dhcp4d/leases.json")
|
||||
b, err := ioutil.ReadFile(path.Join(*perm, "/dhcp4d/leases.json"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -90,6 +94,17 @@ func logic() error {
|
||||
return err
|
||||
}
|
||||
srv.SetLeases(leases)
|
||||
|
||||
b, err = ioutil.ReadFile(path.Join(*perm, "/dns.json"))
|
||||
if err != nil {
|
||||
log.Printf("cannot read DNS entries: %v", err)
|
||||
return nil
|
||||
}
|
||||
var dnsE []dns.IP
|
||||
if err := json.Unmarshal(b, &dnsE); err != nil {
|
||||
return nil
|
||||
}
|
||||
srv.SetDNSEntries(dnsE)
|
||||
return nil
|
||||
}
|
||||
if err := readLeases(); err != nil {
|
||||
|
@ -25,6 +25,7 @@ import (
|
||||
"log"
|
||||
"net"
|
||||
"os"
|
||||
"path"
|
||||
"time"
|
||||
|
||||
"github.com/gokrazy/gokrazy"
|
||||
@ -35,6 +36,8 @@ import (
|
||||
|
||||
var update = dyndns.Update
|
||||
|
||||
var perm = flag.String("perm", "/perm", "path to replace /perm")
|
||||
|
||||
type DynDNSRecord struct {
|
||||
// TODO: multiple providers support
|
||||
Cloudflare struct {
|
||||
@ -105,7 +108,7 @@ func main() {
|
||||
var (
|
||||
configFile = flag.String(
|
||||
"config_file",
|
||||
"/perm/dyndns.json",
|
||||
path.Join(*perm, "/dyndns.json"),
|
||||
"Path to the JSON configuration",
|
||||
)
|
||||
|
||||
|
@ -21,6 +21,7 @@ import (
|
||||
"net/http"
|
||||
"os"
|
||||
"os/signal"
|
||||
"path"
|
||||
"sync"
|
||||
"syscall"
|
||||
|
||||
@ -39,7 +40,9 @@ import (
|
||||
var log = teelogger.NewConsole()
|
||||
|
||||
var (
|
||||
linger = flag.Bool("linger", true, "linger around after applying the configuration (until killed)")
|
||||
linger = flag.Bool("linger", true, "linger around after applying the configuration (until killed)")
|
||||
perm = flag.String("perm", "/perm", "path to replace /perm")
|
||||
noFirewall = flag.Bool("nofirewall", false, "disable the rtr7 firewall")
|
||||
)
|
||||
|
||||
func init() {
|
||||
@ -114,7 +117,7 @@ func updateListeners() error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if net1, err := multilisten.IPv6Net1("/perm"); err == nil {
|
||||
if net1, err := multilisten.IPv6Net1(*perm); err == nil {
|
||||
hosts = append(hosts, net1)
|
||||
}
|
||||
|
||||
@ -134,19 +137,18 @@ func logic() error {
|
||||
ch := make(chan os.Signal, 1)
|
||||
signal.Notify(ch, syscall.SIGUSR1)
|
||||
for {
|
||||
err := netconfig.Apply("/perm/", "/")
|
||||
err := netconfig.Apply(*perm, "/", !*noFirewall)
|
||||
|
||||
// Notify dhcp4d so that it can update its listeners for prometheus
|
||||
// metrics on the external interface.
|
||||
if err := notify.Process("/user/dhcp4d", syscall.SIGUSR1); err != nil {
|
||||
if err := notify.Process(path.Join(path.Dir(os.Args[0]), "dhcp4d"), syscall.SIGUSR1); err != nil {
|
||||
log.Printf("notifying dhcp4d: %v", err)
|
||||
}
|
||||
|
||||
// Notify gokrazy about new addresses (netconfig.Apply might have
|
||||
// modified state before returning an error) so that listeners can be
|
||||
// updated.
|
||||
p, _ := os.FindProcess(1)
|
||||
if err := p.Signal(syscall.SIGHUP); err != nil {
|
||||
if err := notify.Process(path.Join(path.Dir(os.Args[0]), "rtr7-init"), syscall.SIGHUP); err != nil {
|
||||
log.Printf("kill -HUP 1: %v", err)
|
||||
}
|
||||
if err != nil {
|
||||
@ -165,6 +167,7 @@ func logic() error {
|
||||
|
||||
func main() {
|
||||
flag.Parse()
|
||||
netconfig.CmdRoot = path.Dir(os.Args[0])
|
||||
if err := logic(); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
@ -12,7 +12,7 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// Binary radvd sends IPv6 router advertisments.
|
||||
// Binary radvd sends IPv6 router advertisements.
|
||||
package main
|
||||
|
||||
import (
|
||||
@ -23,19 +23,22 @@ import (
|
||||
"net"
|
||||
"os"
|
||||
"os/signal"
|
||||
"path"
|
||||
"syscall"
|
||||
|
||||
"github.com/rtr7/router7/internal/dhcp6"
|
||||
"github.com/rtr7/router7/internal/radvd"
|
||||
)
|
||||
|
||||
var perm = flag.String("perm", "/perm", "path to replace /perm")
|
||||
|
||||
func logic() error {
|
||||
srv, err := radvd.NewServer()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
readConfig := func() error {
|
||||
b, err := ioutil.ReadFile("/perm/dhcp6/wire/lease.json")
|
||||
b, err := ioutil.ReadFile(path.Join(*perm, "/dhcp6/wire/lease.json"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -45,7 +48,7 @@ func logic() error {
|
||||
}
|
||||
|
||||
var additional []net.IPNet
|
||||
if b, err := ioutil.ReadFile("/perm/radvd/prefixes.json"); err == nil {
|
||||
if b, err := ioutil.ReadFile(path.Join(*perm, "/radvd/prefixes.json")); err == nil {
|
||||
if err := json.Unmarshal(b, &additional); err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -1 +0,0 @@
|
||||
router7.org
|
@ -1,221 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html> <head>
|
||||
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
||||
|
||||
|
||||
<link rel="stylesheet" href="/bootstrap-4.4.1.min.css" crossorigin="anonymous">
|
||||
|
||||
|
||||
|
||||
<link rel="stylesheet" href="https://router7.org/sass/sidebar.css">
|
||||
|
||||
<title>router7: architecture</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="content">
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-md-10"><nav class="navbar navbar-expand-lg navbar-light bg-light">
|
||||
<a class="navbar-brand" href="#">router7</a>
|
||||
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarNavAltMarkup" aria-controls="navbarNavAltMarkup" aria-expanded="false" aria-label="Toggle navigation">
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
</button>
|
||||
<div class="collapse navbar-collapse" id="navbarNavAltMarkup">
|
||||
<div class="navbar-nav ml-auto">
|
||||
|
||||
|
||||
|
||||
<a class="nav-item nav-link " href="/">Home </a>
|
||||
|
||||
|
||||
<a class="nav-item nav-link active" href="/architecture/">Architecture <span class="sr-only">(current)</span></a>
|
||||
|
||||
|
||||
<a class="nav-item nav-link " href="/installation/">Installation </a>
|
||||
|
||||
|
||||
<a class="nav-item nav-link " href="https://github.com/rtr7/router7">GitHub </a>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
<h1 id="architecture">Architecture</h1>
|
||||
<p>router7 is based on <a href="https://gokrazy.org/">gokrazy</a>: it is an appliance which gets packed into a hard disk image, containing a FAT partition with the kernel, a read-only SquashFS partition for the root file system and an ext4 partition for permanent data.</p>
|
||||
<p>The individual services can be found in <a href="https://pkg.go.dev/github.com/rtr7/router7/cmd">github.com/rtr7/router7/cmd</a></p>
|
||||
<ul>
|
||||
<li>Each service runs in a separate process.</li>
|
||||
<li>Services communicate with each other by persisting state files. E.g., <code>cmd/dhcp4</code> writes <code>/perm/dhcp4/wire/lease.json</code>.</li>
|
||||
<li>A service notifies other services about state changes by sending them signal <code>SIGUSR1</code>.</li>
|
||||
</ul>
|
||||
<h2 id="configuration-files">Configuration files</h2>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<table class="table table-striped table-bordered">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>File</th>
|
||||
<th>Consumer(s)</th>
|
||||
<th>Purpose</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><code>/perm/interfaces.json</code></td>
|
||||
<td><code>netconfigd</code></td>
|
||||
<td>Set IP/MAC addresses of <code>uplink0</code> and <code>lan0</code></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>/perm/portforwardings.json</code></td>
|
||||
<td><code>netconfigd</code></td>
|
||||
<td>Configure nftables port forwarding rules</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>/perm/dhcp6/duid</code></td>
|
||||
<td><code>dhcp6</code></td>
|
||||
<td>Set DHCP Unique Identifier (DUID) for obtaining static leases</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
|
||||
<h2 id="state-files">State files</h2>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<table class="table table-striped table-bordered">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>File</th>
|
||||
<th>Producer</th>
|
||||
<th>Consumer(s)</th>
|
||||
<th>Purpose</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><code>/perm/dhcp4/wire/ack</code></td>
|
||||
<td><code>dhcp4</code></td>
|
||||
<td><code>dhcp4</code></td>
|
||||
<td>last DHCPACK packet for renewals across restarts</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>/perm/dhcp4/wire/lease.json</code></td>
|
||||
<td><code>dhcp4</code></td>
|
||||
<td><code>netconfigd</code></td>
|
||||
<td>Obtained DHCPv4 lease</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>/perm/dhcp6/wire/lease.json</code></td>
|
||||
<td><code>dhcp6</code></td>
|
||||
<td><code>netconfigd</code>, <code>radvd</code></td>
|
||||
<td>Obtained DHCPv6 lease</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>/perm/dhcp4d/leases.json</code></td>
|
||||
<td><code>dhcp4d</code></td>
|
||||
<td><code>dhcp4d</code>, <code>dnsd</code></td>
|
||||
<td>DHCPv4 leases handed out (including hostnames)</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
|
||||
<h2 id="available-ports">Available ports</h2>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<table class="table table-striped table-bordered">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Port</th>
|
||||
<th>Purpose</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><code><public>:8053</code></td>
|
||||
<td><code>dnsd</code> metrics (forwarded requests)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code><public>:8066</code></td>
|
||||
<td><code>netconfigd</code> metrics (nftables counters)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code><private>:80</code></td>
|
||||
<td>gokrazy web interface</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code><private>:67</code></td>
|
||||
<td><code>dhcp4d</code></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code><private>:58</code></td>
|
||||
<td><code>radvd</code></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code><private>:53</code></td>
|
||||
<td><code>dnsd</code></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code><private>:8077</code></td>
|
||||
<td><code>backupd</code> (serve backup.tar.gz)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code><private>:7733</code></td>
|
||||
<td><code>diagd</code> (perform diagnostics)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code><private>:5022</code></td>
|
||||
<td><code>captured</code> (serve captured packets)</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
|
||||
<p>Here’s an example of <code>cmd/diagd</code> output:</p>
|
||||
<p><img src="https://github.com/rtr7/router7/raw/master/2018-07-14-diagd.png"
|
||||
width="800" alt="diagd output"></p>
|
||||
<p>Here’s an example of <code>cmd/netconfigd</code> metrics when scraped with <a href="https://prometheus.io/">Prometheus</a> and displayed in <a href="https://grafana.com/">Grafana</a>:</p>
|
||||
<p><img src="https://github.com/rtr7/router7/raw/master/2018-07-14-grafana.png"
|
||||
width="800" alt="metrics in grafana"></p>
|
||||
|
||||
|
||||
<hr>
|
||||
|
||||
<p class="small">
|
||||
© 2018 Michael Stapelberg and contributors
|
||||
</p>
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
<aside class="bd-toc">
|
||||
<nav id="TableOfContents">
|
||||
<ul>
|
||||
<li><a href="#configuration-files">Configuration files</a></li>
|
||||
<li><a href="#state-files">State files</a></li>
|
||||
<li><a href="#available-ports">Available ports</a></li>
|
||||
</ul>
|
||||
</nav>
|
||||
</aside>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="/jquery-3.4.1.slim.min.js" integrity="sha384-J6qa4849blE2+poT4WnyKhv5vZF5SrPo0iEjwBvKU7imGFAV0wwj1yYfoRSJoZ+n" crossorigin="anonymous"></script>
|
||||
<script src="/popper-1.16.0.min.js" integrity="sha384-Q6E9RHvbIyZFJoft+2mJbHaEWldlvI9IOYy5n3zV9zzTtmI3UksdQRVvoxMfooAo" crossorigin="anonymous"></script>
|
||||
<script src="/bootstrap-4.4.1.min.js" integrity="sha384-wfSDF2E50Y2D1uUdj0O3uMBJnjuUD4Ih7YwaYd1iqfktj0Uod8GCExl3Og8ifwB6" crossorigin="anonymous"></script>
|
||||
</body>
|
||||
</html>
|
||||
</body>
|
||||
</html>
|
7
docs/bootstrap-4.4.1.min.css
vendored
7
docs/bootstrap-4.4.1.min.css
vendored
File diff suppressed because one or more lines are too long
7
docs/bootstrap-4.4.1.min.js
vendored
7
docs/bootstrap-4.4.1.min.js
vendored
File diff suppressed because one or more lines are too long
@ -1,94 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html> <head>
|
||||
<meta name="generator" content="Hugo 0.71.0-DEV" />
|
||||
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
||||
|
||||
|
||||
<link rel="stylesheet" href="/bootstrap-4.4.1.min.css" crossorigin="anonymous">
|
||||
|
||||
|
||||
|
||||
<link rel="stylesheet" href="https://router7.org/sass/sidebar.css">
|
||||
|
||||
<title>router7: a small home internet router completely written in Go</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="content">
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-md-10"><nav class="navbar navbar-expand-lg navbar-light bg-light">
|
||||
<a class="navbar-brand" href="#">router7</a>
|
||||
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarNavAltMarkup" aria-controls="navbarNavAltMarkup" aria-expanded="false" aria-label="Toggle navigation">
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
</button>
|
||||
<div class="collapse navbar-collapse" id="navbarNavAltMarkup">
|
||||
<div class="navbar-nav ml-auto">
|
||||
|
||||
|
||||
|
||||
<a class="nav-item nav-link active" href="/">Home <span class="sr-only">(current)</span></a>
|
||||
|
||||
|
||||
<a class="nav-item nav-link " href="/architecture/">Architecture </a>
|
||||
|
||||
|
||||
<a class="nav-item nav-link " href="/installation/">Installation </a>
|
||||
|
||||
|
||||
<a class="nav-item nav-link " href="https://github.com/rtr7/router7">GitHub </a>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
<h1 id="router7">router7</h1>
|
||||
<p>router7 is a pure-Go implementation of a small home internet router. It comes with all the services required to make a <a href="https://www.init7.net/en/internet/fiber7/">fiber7 internet connection</a> work (DHCPv4, DHCPv6, DNS, etc.).</p>
|
||||
<p>Note that this project should be considered a (working!) tech demo. Feature requests will likely not be implemented, and see <a href="CONTRIBUTING.md">CONTRIBUTING.md</a> for details about which contributions are welcome.</p>
|
||||
<h2 id="motivation">Motivation</h2>
|
||||
<p>Before starting router7, I was using the <a href="https://omnia.turris.cz/en/">Turris Omnia</a> router running OpenWrt. That worked fine up until May 2018, when an automated update pulled in a new version of <a href="https://git.openwrt.org/?p=project/odhcp6c.git;a=shortlog">odhcp6c</a>, OpenWrt’s DHCPv6 client. That version is incompatible with fiber7’s DHCP server setup (I think there are shortcomings on both sides).</p>
|
||||
<p>It was not only quicker to develop my own router than to wait for either side to resolve the issue, but it was also a lot of fun and allowed me to really tailor my router to my needs, experimenting with a bunch of interesting ideas I had.</p>
|
||||
<h2 id="project-goals">Project goals</h2>
|
||||
<ul>
|
||||
<li>Maximize internet connectivity: retain the most recent DHCP configuration across reboots and even after its expiration (chances are the DHCP server will be back before the configuration stops working).</li>
|
||||
<li>Unit/integration tests use fiber7 packet capture files to minimize the chance of software changes breaking my connectivity.</li>
|
||||
<li>Safe and quick updates
|
||||
<ul>
|
||||
<li>Auto-rollback of updates which result in loss of connectivity: the diagnostics daemon assesses connectivity state, the update tool reads it and rolls back faulty updates.</li>
|
||||
<li>Thanks to kexec, updates translate into merely 13s of internet connectivity loss.</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>Easy debugging
|
||||
<ul>
|
||||
<li>Configuration-related network packets (e.g. DHCP, IPv6 neighbor/router advertisements) are stored in a ring buffer which can be streamed into <a href="https://www.wireshark.org/">Wireshark</a>, allowing for live and retro-active debugging.</li>
|
||||
<li>The diagnostics daemon performs common diagnostic steps (ping, traceroute, …) for you.</li>
|
||||
<li>All state in the system is stored as human-readable JSON within the <code>/perm</code> partition and can be modified.</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
<h2 id="hardware">Hardware</h2>
|
||||
<p>The reference hardware platform is the <a href="https://pcengines.ch/apu2c4.htm">PC Engines™ apu2c4</a> system board. It features a 1 GHz quad core amd64 CPU, 4 GB of RAM, 3 Ethernet ports and a DB9 serial port. It conveniently supports PXE boot, the schematics and bootloader sources are available. I recommend the <a href="https://pcengines.ch/msata16g.htm">msata16g</a> SSD module for reliable persistent storage and the <a href="https://pcengines.ch/usbcom1a.htm">usbcom1a</a> serial adapter if you don’t have one already.</p>
|
||||
<p>Other hardware might work, too, but is not tested.</p>
|
||||
|
||||
|
||||
<hr>
|
||||
|
||||
<p class="small">
|
||||
© 2018 Michael Stapelberg and contributors
|
||||
</p>
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="/jquery-3.4.1.slim.min.js" integrity="sha384-J6qa4849blE2+poT4WnyKhv5vZF5SrPo0iEjwBvKU7imGFAV0wwj1yYfoRSJoZ+n" crossorigin="anonymous"></script>
|
||||
<script src="/popper-1.16.0.min.js" integrity="sha384-Q6E9RHvbIyZFJoft+2mJbHaEWldlvI9IOYy5n3zV9zzTtmI3UksdQRVvoxMfooAo" crossorigin="anonymous"></script>
|
||||
<script src="/bootstrap-4.4.1.min.js" integrity="sha384-wfSDF2E50Y2D1uUdj0O3uMBJnjuUD4Ih7YwaYd1iqfktj0Uod8GCExl3Og8ifwB6" crossorigin="anonymous"></script>
|
||||
</body>
|
||||
</html>
|
||||
</body>
|
||||
</html>
|
@ -1,146 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html> <head>
|
||||
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
||||
|
||||
|
||||
<link rel="stylesheet" href="/bootstrap-4.4.1.min.css" crossorigin="anonymous">
|
||||
|
||||
|
||||
|
||||
<link rel="stylesheet" href="https://router7.org/sass/sidebar.css">
|
||||
|
||||
<title>router7: installation</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="content">
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-md-10"><nav class="navbar navbar-expand-lg navbar-light bg-light">
|
||||
<a class="navbar-brand" href="#">router7</a>
|
||||
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarNavAltMarkup" aria-controls="navbarNavAltMarkup" aria-expanded="false" aria-label="Toggle navigation">
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
</button>
|
||||
<div class="collapse navbar-collapse" id="navbarNavAltMarkup">
|
||||
<div class="navbar-nav ml-auto">
|
||||
|
||||
|
||||
|
||||
<a class="nav-item nav-link " href="/">Home </a>
|
||||
|
||||
|
||||
<a class="nav-item nav-link " href="/architecture/">Architecture </a>
|
||||
|
||||
|
||||
<a class="nav-item nav-link active" href="/installation/">Installation <span class="sr-only">(current)</span></a>
|
||||
|
||||
|
||||
<a class="nav-item nav-link " href="https://github.com/rtr7/router7">GitHub </a>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
<h1 id="installation">Installation</h1>
|
||||
<p>Connect your serial adapter (<a href="https://pcengines.ch/usbcom1a.htm">usbcom1a</a> works well if you don’t have one already) to the apu2c4 and start a program to use it, e.g. <code>screen /dev/ttyUSB0 115200</code>. Then, power on the apu2c4 and configure it to do PXE boot:</p>
|
||||
<ul>
|
||||
<li>Press <code>F10</code> to enter the boot menu</li>
|
||||
<li>Press <code>3</code> to enter setup</li>
|
||||
<li>Press <code>n</code> to enable network boot</li>
|
||||
<li>Press <code>c</code> to move mSATA to the top of the boot order</li>
|
||||
<li>Press <code>e</code> to move iPXE to the top of the boot order</li>
|
||||
<li>Press <code>s</code> to save configuration and exit</li>
|
||||
</ul>
|
||||
<p>Connect a network cable on <code>net0</code>, the port closest to the serial console port:</p>
|
||||
<p><img src="https://github.com/rtr7/router7/raw/master/devsetup.jpg"
|
||||
width="800" alt="router7 development setup"></p>
|
||||
<p>Next, build a router7 image:</p>
|
||||
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-shell" data-lang="shell">go get -u github.com/gokrazy/tools/cmd/gokr-packer github.com/rtr7/tools/cmd/...
|
||||
go get -u -d github.com/rtr7/router7
|
||||
mkdir /tmp/recovery
|
||||
GOARCH<span style="color:#f92672">=</span>amd64 gokr-packer <span style="color:#ae81ff">\
|
||||
</span><span style="color:#ae81ff"></span> -hostname<span style="color:#f92672">=</span>router7 <span style="color:#ae81ff">\
|
||||
</span><span style="color:#ae81ff"></span> -overwrite_boot<span style="color:#f92672">=</span>/tmp/recovery/boot.img <span style="color:#ae81ff">\
|
||||
</span><span style="color:#ae81ff"></span> -overwrite_mbr<span style="color:#f92672">=</span>/tmp/recovery/mbr.img <span style="color:#ae81ff">\
|
||||
</span><span style="color:#ae81ff"></span> -overwrite_root<span style="color:#f92672">=</span>/tmp/recovery/root.img <span style="color:#ae81ff">\
|
||||
</span><span style="color:#ae81ff"></span> -kernel_package<span style="color:#f92672">=</span>github.com/rtr7/kernel <span style="color:#ae81ff">\
|
||||
</span><span style="color:#ae81ff"></span> -firmware_package<span style="color:#f92672">=</span>github.com/rtr7/kernel <span style="color:#ae81ff">\
|
||||
</span><span style="color:#ae81ff"></span> -gokrazy_pkgs<span style="color:#f92672">=</span>github.com/gokrazy/gokrazy/cmd/ntp <span style="color:#ae81ff">\
|
||||
</span><span style="color:#ae81ff"></span> -serial_console<span style="color:#f92672">=</span>ttyS0,115200n8 <span style="color:#ae81ff">\
|
||||
</span><span style="color:#ae81ff"></span> github.com/rtr7/router7/cmd/...
|
||||
</code></pre></div><p>Run <code>rtr7-recover -boot=/tmp/recovery/boot.img -mbr=/tmp/recovery/mbr.img -root=/tmp/recovery/root.img</code> to:</p>
|
||||
<ul>
|
||||
<li>trigger a reset <a href="#rebootor">if a Teensy with the rebootor firmware is attached</a></li>
|
||||
<li>serve a DHCP lease to all clients which request PXE boot (i.e., your apu2c4)</li>
|
||||
<li>serve via TFTP:
|
||||
<ul>
|
||||
<li>the PXELINUX bootloader</li>
|
||||
<li>the router7 kernel</li>
|
||||
<li>an initrd archive containing the rtr7-recovery-init program and mke2fs</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>serve via HTTP the boot and root images</li>
|
||||
<li>optionally serve via HTTP a backup.tar.gz image containing files for <code>/perm</code> (e.g. for moving to new hardware, rolling back corrupted state, or recovering from a disk failure)</li>
|
||||
<li>exit once the router successfully wrote the images to disk</li>
|
||||
</ul>
|
||||
<h2 id="updates">Updates</h2>
|
||||
<p>Run e.g. <code>rtr7-safe-update -updates_dir=$HOME/router7/updates</code> to:</p>
|
||||
<ul>
|
||||
<li>verify the router currently has connectivity, abort the update otherwise</li>
|
||||
<li>download a backup archive of <code>/perm</code></li>
|
||||
<li>build a new image</li>
|
||||
<li>update the router</li>
|
||||
<li>wait until the router restored connectivity, roll back the update using <code>rtr7-recover</code> otherwise</li>
|
||||
</ul>
|
||||
<p>The update step uses kexec to reduce the downtime to approximately 15 seconds.</p>
|
||||
<h2 id="manual-recovery">Manual Recovery</h2>
|
||||
<p>Given <code>rtr7-safe-update</code>’s safeguards, manual recovery should rarely be required.</p>
|
||||
<p>To manually roll back to an older image, invoke <code>rtr7-safe-update</code> via the
|
||||
<code>recover.bash</code> script in the image directory underneath <code>-updates_dir</code>, e.g.:</p>
|
||||
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-shell" data-lang="shell">% cd ~/router7/updates/2018-07-03T17:33:52+02:00
|
||||
% ./recover.bash
|
||||
</code></pre></div><h2 id="rebootor">Teensy rebootor</h2>
|
||||
<p>The cheap and widely-available <a href="https://www.pjrc.com/store/teensypp.html">Teensy++ USB development board</a> comes with a firmware called rebootor, which is used by the <a href="https://www.pjrc.com/teensy/loader_cli.html"><code>teensy_loader_cli</code></a> program to perform hard resets.</p>
|
||||
<p>This setup can be used to programmatically reset the apu2c4 (from <code>rtr7-recover</code>) by connecting the Teensy++ to the <a href="http://pcengines.ch/pdf/apu2.pdf">apu2c4’s reset pins</a>:</p>
|
||||
<ul>
|
||||
<li>connect the Teensy++’s <code>GND</code> pin to the apu2c4 J2’s pin 4 (<code>GND</code>)</li>
|
||||
<li>connect the Teensy++’s <code>B7</code> pin to the apu2c4 J2’s pin 5 (<code>3.3V</code>, resets when pulled to <code>GND</code>)</li>
|
||||
</ul>
|
||||
<p>You can find a working rebootor firmware .hex file at <a href="https://github.com/PaulStoffregen/teensy_loader_cli/issues/38">https://github.com/PaulStoffregen/teensy_loader_cli/issues/38</a></p>
|
||||
<h2 id="prometheus">Prometheus</h2>
|
||||
<p>See <a href="https://github.com/rtr7/router7/tree/master/contrib/prometheus">https://github.com/rtr7/router7/tree/master/contrib/prometheus</a> for example
|
||||
configuration files, and install the <a href="https://grafana.com/dashboards/8288">router7 Grafana
|
||||
Dashboard</a>.</p>
|
||||
|
||||
|
||||
<hr>
|
||||
|
||||
<p class="small">
|
||||
© 2018 Michael Stapelberg and contributors
|
||||
</p>
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
<aside class="bd-toc">
|
||||
<nav id="TableOfContents">
|
||||
<ul>
|
||||
<li><a href="#updates">Updates</a></li>
|
||||
<li><a href="#manual-recovery">Manual Recovery</a></li>
|
||||
<li><a href="#rebootor">Teensy rebootor</a></li>
|
||||
<li><a href="#prometheus">Prometheus</a></li>
|
||||
</ul>
|
||||
</nav>
|
||||
</aside>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="/jquery-3.4.1.slim.min.js" integrity="sha384-J6qa4849blE2+poT4WnyKhv5vZF5SrPo0iEjwBvKU7imGFAV0wwj1yYfoRSJoZ+n" crossorigin="anonymous"></script>
|
||||
<script src="/popper-1.16.0.min.js" integrity="sha384-Q6E9RHvbIyZFJoft+2mJbHaEWldlvI9IOYy5n3zV9zzTtmI3UksdQRVvoxMfooAo" crossorigin="anonymous"></script>
|
||||
<script src="/bootstrap-4.4.1.min.js" integrity="sha384-wfSDF2E50Y2D1uUdj0O3uMBJnjuUD4Ih7YwaYd1iqfktj0Uod8GCExl3Og8ifwB6" crossorigin="anonymous"></script>
|
||||
</body>
|
||||
</html>
|
||||
</body>
|
||||
</html>
|
2
docs/jquery-3.4.1.slim.min.js
vendored
2
docs/jquery-3.4.1.slim.min.js
vendored
File diff suppressed because one or more lines are too long
5
docs/popper-1.16.0.min.js
vendored
5
docs/popper-1.16.0.min.js
vendored
File diff suppressed because one or more lines are too long
@ -1,2 +0,0 @@
|
||||
User-Agent: *
|
||||
sitemap: https://router7.org/sitemap.xml
|
@ -1,19 +0,0 @@
|
||||
.bd-toc {
|
||||
position: sticky;
|
||||
top: 4rem;
|
||||
height: calc(100vh - 4rem);
|
||||
overflow-y: auto; }
|
||||
|
||||
.bd-toc ul {
|
||||
list-style: none;
|
||||
padding-left: 1em;
|
||||
border-left: 1px solid #eee; }
|
||||
|
||||
.bd-toc li {
|
||||
margin-top: 1em;
|
||||
margin-bottom: 1em; }
|
||||
|
||||
/* TODO: move this to a separate style sheet */
|
||||
.bigbutton {
|
||||
margin-left: 1em;
|
||||
margin-right: 1em; }
|
@ -1,17 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
|
||||
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"
|
||||
xmlns:xhtml="http://www.w3.org/1999/xhtml">
|
||||
|
||||
<url>
|
||||
<loc>https://router7.org/</loc>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://router7.org/architecture/</loc>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://router7.org/installation/</loc>
|
||||
</url>
|
||||
|
||||
</urlset>
|
16
go.mod
16
go.mod
@ -4,36 +4,27 @@ go 1.13
|
||||
|
||||
require (
|
||||
github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883
|
||||
github.com/beevik/ntp v0.2.0 // indirect
|
||||
github.com/cloudflare/cloudflare-go v0.11.4 // indirect
|
||||
github.com/digineo/go-ping v1.0.0
|
||||
github.com/gokrazy/breakglass v0.0.0-20190928090743-3bc0b096353f // indirect
|
||||
github.com/gokrazy/gdns v0.0.0-20200218203540-6b3b6244ea39 // indirect
|
||||
github.com/gokrazy/gokrazy v0.0.0-20200501080617-f3445e01a904
|
||||
github.com/gokrazy/internal v0.0.0-20200407080221-9da902858268 // indirect
|
||||
github.com/gokrazy/timestamps v0.0.0-20200407065656-4b59e6dc085f // indirect
|
||||
github.com/gokrazy/tools v0.0.0-20200409073635-887bfb76f1b1 // indirect
|
||||
github.com/golang/protobuf v1.4.1 // indirect
|
||||
github.com/google/go-cmp v0.4.0
|
||||
github.com/google/gopacket v1.1.17
|
||||
github.com/google/nftables v0.0.0-20200316075819-7127d9d22474
|
||||
github.com/google/renameio v0.1.0
|
||||
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect
|
||||
github.com/insomniacslk/dhcp v0.0.0-20200420235442-ed3125c2efe7
|
||||
github.com/jpillora/backoff v1.0.0
|
||||
github.com/kr/text v0.2.0 // indirect
|
||||
github.com/krolaw/dhcp4 v0.0.0-20190909130307-a50d88189771
|
||||
github.com/libdns/cloudflare v0.0.0-20200528144945-97886e7873b1
|
||||
github.com/libdns/libdns v0.0.0-20200501023120-186724ffc821
|
||||
github.com/mdlayher/ndp v0.0.0-20200509194142-8a50b5ef8b52
|
||||
github.com/mdlayher/raw v0.0.0-20191009151244-50f2db8cc065
|
||||
github.com/miekg/dns v1.1.29
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect
|
||||
github.com/prometheus/client_golang v1.6.0
|
||||
github.com/prometheus/procfs v0.0.11 // indirect
|
||||
github.com/rtr7/dhcp4 v0.0.0-20181120124042-778e8c2e24a5
|
||||
github.com/rtr7/dyndns v0.0.0-20190106135032-c057db8a5daf // indirect
|
||||
github.com/rtr7/kernel v0.0.0-20200506151338-66f9d4444856 // indirect
|
||||
github.com/sergi/go-diff v1.1.0 // indirect
|
||||
github.com/stapelberg/zkj-nas-tools v0.0.0-20200309084414-3f5eb432164d // indirect
|
||||
github.com/u-root/u-root v6.0.0+incompatible // indirect
|
||||
github.com/vishvananda/netlink v1.1.0
|
||||
golang.org/x/crypto v0.0.0-20200429183012-4b2356b1ed79
|
||||
@ -43,5 +34,6 @@ require (
|
||||
golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1
|
||||
golang.zx2c4.com/wireguard v0.0.20200320 // indirect
|
||||
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20200324154536-ceff61240acf
|
||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f // indirect
|
||||
gopkg.in/yaml.v2 v2.2.8 // indirect
|
||||
)
|
||||
|
||||
|
205
go.sum
205
go.sum
@ -1,25 +1,18 @@
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/DATA-DOG/go-txdb v0.1.3/go.mod h1:DhAhxMXZpUJVGnT+p9IbzJoRKvlArO2pkHjnGX7o0n0=
|
||||
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||
github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||
github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883 h1:bvNMNQO63//z+xNgfBlViaCIJKLlCJ6/fmUseuG0wVQ=
|
||||
github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8=
|
||||
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239 h1:kFOfPq6dUM1hTo4JG6LR5AXSUEsOjtdm0kw0FtQtMJA=
|
||||
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c=
|
||||
github.com/beevik/ntp v0.2.0 h1:sGsd+kAXzT0bfVfzJfce04g+dSRfrs+tbQW8lweuYgw=
|
||||
github.com/beevik/ntp v0.2.0/go.mod h1:hIHWr+l3+/clUnF44zdK+CWW7fO8dR5cIylAQ76NRpg=
|
||||
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
||||
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
|
||||
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
||||
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
||||
github.com/cespare/xxhash/v2 v2.1.0/go.mod h1:dgIUBU3pDso/gPgZ1osOZ0iQf77oPR28Tjxl5dIMyVM=
|
||||
github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY=
|
||||
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/cloudflare/cloudflare-go v0.11.4 h1:A6hBhFnyGVvB0omIOM5+w7p3nftn80zFQPzvhSusdGc=
|
||||
github.com/cloudflare/cloudflare-go v0.11.4/go.mod h1:ZB+hp7VycxPLpp0aiozQQezat46npDXhzHi1DVtRCn4=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
@ -27,53 +20,25 @@ 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-ping v1.0.0 h1:gOuD3YzkIcW/0Y2IAe27bsMKtpfNZdoX1Rnc1RGYOSI=
|
||||
github.com/digineo/go-ping v1.0.0/go.mod h1:YLDBnHoAygacawa2aubI4vXhZ4do5f62oJSvRiJVEjw=
|
||||
github.com/eclipse/paho.mqtt.golang v1.1.1/go.mod h1:H9keYFcgq3Qr5OUJm/JZI/i6U7joQ8SYLhZwfeOo6Ts=
|
||||
github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys=
|
||||
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
||||
github.com/fearful-symmetry/garlic v0.3.0/go.mod h1:+hcj5tRGZdwY6RKMopfdnlMAMepQz4Nbaw7A12MCNWk=
|
||||
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
|
||||
github.com/gdamore/encoding v1.0.0/go.mod h1:alR0ol34c49FCSBLjhosxzcPHQbf2trDkoo5dl+VrEg=
|
||||
github.com/gdamore/tcell v1.1.1/go.mod h1:K1udHkiR3cOtlpKG5tZPD5XxrF7v2y7lDq7Whcj+xkQ=
|
||||
github.com/gliderlabs/ssh v0.2.2 h1:6zsha5zo/TWhRhwqCD3+EarCAgZ2yN28ipRnGPnwkI0=
|
||||
github.com/gliderlabs/ssh v0.2.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=
|
||||
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||
github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
|
||||
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
|
||||
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||
github.com/gokrazy/breakglass v0.0.0-20190928090743-3bc0b096353f h1:ItCCHlgoMKSzKXtTmR+kbly06tEMrCq62sYeqcXpDd4=
|
||||
github.com/gokrazy/breakglass v0.0.0-20190928090743-3bc0b096353f/go.mod h1:fs/x7yPFXPkydb39m2jN/csIobdQCGGhLjACvRGQ8xE=
|
||||
github.com/gokrazy/gdns v0.0.0-20200218203540-6b3b6244ea39 h1:RZymiPOJZjAPHxzQAPplCSVpLRPfQjYf/iRPGBCRQbY=
|
||||
github.com/gokrazy/gdns v0.0.0-20200218203540-6b3b6244ea39/go.mod h1:C9KcIuxFAeBatA48aTq6gRj2o2UV/Z7C+wzt43sXtQE=
|
||||
github.com/gokrazy/gokrazy v0.0.0-20190321081644-520b8ca41de7/go.mod h1:YbpshsItGhDXnytFAvMTRvZvGkVSpZV/4mwxQsvqzHg=
|
||||
github.com/gokrazy/gokrazy v0.0.0-20191210211542-6beb2e16aa3a h1:1tv1YMuZ+6vHZwCWcaILUIrjAfUdgXYrDxenJusCvqs=
|
||||
github.com/gokrazy/gokrazy v0.0.0-20191210211542-6beb2e16aa3a/go.mod h1:YbpshsItGhDXnytFAvMTRvZvGkVSpZV/4mwxQsvqzHg=
|
||||
github.com/gokrazy/gokrazy v0.0.0-20200407080001-6bbd4a085cfa h1:UKN7QQbFNz7xmI2e7CfAuORzH6wjgXM6N1utvyOkkl4=
|
||||
github.com/gokrazy/gokrazy v0.0.0-20200407080001-6bbd4a085cfa/go.mod h1:pq6rGHqxMRPSaTXaCMzIZy0wLDusAJyoVNyNo05RLs0=
|
||||
github.com/gokrazy/gokrazy v0.0.0-20200408132436-3a6c5f85d259 h1:P4MC73hJ7zx2xaYkAcRsfbTVlbVzqETBpANQssEjhSo=
|
||||
github.com/gokrazy/gokrazy v0.0.0-20200408132436-3a6c5f85d259/go.mod h1:pq6rGHqxMRPSaTXaCMzIZy0wLDusAJyoVNyNo05RLs0=
|
||||
github.com/gokrazy/gokrazy v0.0.0-20200501080617-f3445e01a904 h1:eqfH4A/LLgxv5RvqEXwVoFvfmpRa8+TokRjB5g6xBkk=
|
||||
github.com/gokrazy/gokrazy v0.0.0-20200501080617-f3445e01a904/go.mod h1:pq6rGHqxMRPSaTXaCMzIZy0wLDusAJyoVNyNo05RLs0=
|
||||
github.com/gokrazy/internal v0.0.0-20190630091051-de21a662e434 h1:3NgMIyCbCOWhjO/9/yIloXsQCuP6MLolya2SItd1NCM=
|
||||
github.com/gokrazy/internal v0.0.0-20190630091051-de21a662e434/go.mod h1:c7C8E8dlEJG/vdLtGN5NJPdbKNzZi/puMD0sKC346TI=
|
||||
github.com/gokrazy/internal v0.0.0-20200407065509-37efc446ad44/go.mod h1:LA5TQy7LcvYGQOy75tkrYkFUhbV2nl5qEBP47PSi2JA=
|
||||
github.com/gokrazy/internal v0.0.0-20200407075822-660ad467b7c9/go.mod h1:LA5TQy7LcvYGQOy75tkrYkFUhbV2nl5qEBP47PSi2JA=
|
||||
github.com/gokrazy/internal v0.0.0-20200407080221-9da902858268 h1:Q0Z5vi1HjXMlwiIaC6nn04y0PwRjyG9h9S4hZVzFjTw=
|
||||
github.com/gokrazy/internal v0.0.0-20200407080221-9da902858268/go.mod h1:LA5TQy7LcvYGQOy75tkrYkFUhbV2nl5qEBP47PSi2JA=
|
||||
github.com/gokrazy/timestamps v0.0.0-20180714213742-781e87eed284 h1:NmTSDZEfX+su2CKXOlCOWmAKkRq5CFkm7FHytZ5rSXA=
|
||||
github.com/gokrazy/timestamps v0.0.0-20180714213742-781e87eed284/go.mod h1:8AJQXHQyzPPic6Ke4tN1kSDvpqZtcxjnvlXkhKI50fQ=
|
||||
github.com/gokrazy/timestamps v0.0.0-20200407065656-4b59e6dc085f h1:4psuZaRYF4AwXenDziXRjM3EYTNpH0pKHLH58uFRpfs=
|
||||
github.com/gokrazy/timestamps v0.0.0-20200407065656-4b59e6dc085f/go.mod h1:c7Zy1SHOu7afTaLGjm9HWVUHoQXV7qmMbU95mDKSQVY=
|
||||
github.com/gokrazy/tools v0.0.0-20200116213808-27bcb247d975 h1:oZNWCyENUqDJ1mzpCFAM8JSFfXJ3xrfTF6ELsj+du4c=
|
||||
github.com/gokrazy/tools v0.0.0-20200116213808-27bcb247d975/go.mod h1:BrVlnvSr6tqR03Jpevgvr53UvyJ1FprKhYLllo1QgNE=
|
||||
github.com/gokrazy/tools v0.0.0-20200409073635-887bfb76f1b1 h1:HNb+VNn6ektbGnPhsz3cyjIIIoOJEKqZqWiL5v94Ibc=
|
||||
github.com/gokrazy/tools v0.0.0-20200409073635-887bfb76f1b1/go.mod h1:BrVlnvSr6tqR03Jpevgvr53UvyJ1FprKhYLllo1QgNE=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs=
|
||||
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.5 h1:F768QJ1E9tib+q5Sc8MkdJi1RxLTbRcTf8LJV56aRls=
|
||||
github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk=
|
||||
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
|
||||
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
|
||||
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
|
||||
@ -87,28 +52,15 @@ github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMyw
|
||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4=
|
||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.4.1 h1:/exdXoGamhu5ONeUJH0deniYLWYvQwW66yvlfiiKTu0=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/gopacket v1.1.16/go.mod h1:UCLx9mCmAwsVbn6qQl1WIEt2SO7Nd2fD0th1TBAsqBw=
|
||||
github.com/google/gopacket v1.1.17 h1:rMrlX2ZY2UbvT+sdz3+6J+pp2z+msCq9MxTU6ymxbBY=
|
||||
github.com/google/gopacket v1.1.17/go.mod h1:UdDNZ1OO62aGYVnPhxT1U6aI7ukYtA/kB8vaU0diBUM=
|
||||
github.com/google/nftables v0.0.0-20200210101420-1c56a1906fbf h1:7LRYMcY5YBAZa9aoaIIvwS934JNbPyYmRy/JYZcL/XA=
|
||||
github.com/google/nftables v0.0.0-20200210101420-1c56a1906fbf/go.mod h1:cfspEyr/Ap+JDIITA+N9a0ernqG0qZ4W1aqMRgDZa1g=
|
||||
github.com/google/nftables v0.0.0-20200316075819-7127d9d22474 h1:D6bN82zzK92ywYsE+Zjca7EHZCRZbcNTU3At7WdxQ+c=
|
||||
github.com/google/nftables v0.0.0-20200316075819-7127d9d22474/go.mod h1:cfspEyr/Ap+JDIITA+N9a0ernqG0qZ4W1aqMRgDZa1g=
|
||||
github.com/google/renameio v0.1.0 h1:GOZbcHa3HfsPKPlmyPyN2KEohoMXOhdMbHrvbpl2QaA=
|
||||
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
||||
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4=
|
||||
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ=
|
||||
github.com/insomniacslk/dhcp v0.0.0-20200210095418-45e5f320b2f0 h1:jzkAy3xl8j58ylC1cleuFZyBDCGy+swFc0cdxvVawkc=
|
||||
github.com/insomniacslk/dhcp v0.0.0-20200210095418-45e5f320b2f0/go.mod h1:CfMdguCK66I5DAUJgGKyNz8aB6vO5dZzkm9Xep6WGvw=
|
||||
github.com/insomniacslk/dhcp v0.0.0-20200306230118-99cbb09fb7b9 h1:5gifC0gFQ6VowQOXA1Yn1z4NFXlWRLbDT3oFxIZowJk=
|
||||
github.com/insomniacslk/dhcp v0.0.0-20200306230118-99cbb09fb7b9/go.mod h1:CfMdguCK66I5DAUJgGKyNz8aB6vO5dZzkm9Xep6WGvw=
|
||||
github.com/insomniacslk/dhcp v0.0.0-20200311205251-eed709df9494 h1:KDhUkNE73wlQNDk9xW8anpphV9vDiPZNwfgVC5yHmhk=
|
||||
github.com/insomniacslk/dhcp v0.0.0-20200311205251-eed709df9494/go.mod h1:CfMdguCK66I5DAUJgGKyNz8aB6vO5dZzkm9Xep6WGvw=
|
||||
github.com/insomniacslk/dhcp v0.0.0-20200328160319-5763873e2f3b h1:uMYNezLqdu4pWFJnhvpH7mkXhpe6cMWx6b0ozgnr7AI=
|
||||
github.com/insomniacslk/dhcp v0.0.0-20200328160319-5763873e2f3b/go.mod h1:CfMdguCK66I5DAUJgGKyNz8aB6vO5dZzkm9Xep6WGvw=
|
||||
github.com/insomniacslk/dhcp v0.0.0-20200402185128-5dd7202f1971 h1:P1pxzF2xvdnSY12ODSSwjxA4tyEjDEJNn829OXKnqks=
|
||||
github.com/insomniacslk/dhcp v0.0.0-20200402185128-5dd7202f1971/go.mod h1:CfMdguCK66I5DAUJgGKyNz8aB6vO5dZzkm9Xep6WGvw=
|
||||
github.com/insomniacslk/dhcp v0.0.0-20200420235442-ed3125c2efe7 h1:iaCm+9nZdYb8XCSU2TfIb0qYTcAlIv2XzyKR2d2xZ38=
|
||||
github.com/insomniacslk/dhcp v0.0.0-20200420235442-ed3125c2efe7/go.mod h1:CfMdguCK66I5DAUJgGKyNz8aB6vO5dZzkm9Xep6WGvw=
|
||||
github.com/jpillora/backoff v1.0.0 h1:uvFg412JmmHBHw7iwprIxkPMI+sGQ4kzOWsMeHnm2EA=
|
||||
@ -117,11 +69,11 @@ github.com/jsimonetti/rtnetlink v0.0.0-20190606172950-9527aa82566a/go.mod h1:Oz+
|
||||
github.com/jsimonetti/rtnetlink v0.0.0-20200117123717-f846d4f6c1f4 h1:nwOc1YaOrYJ37sEBrtWZrdqzK22hiJs3GpDmP3sR2Yw=
|
||||
github.com/jsimonetti/rtnetlink v0.0.0-20200117123717-f846d4f6c1f4/go.mod h1:WGuG/smIU4J/54PblvSbh+xvCZmpJnFgr3ds6Z55XMQ=
|
||||
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||
github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||
github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
|
||||
github.com/koneu/natend v0.0.0-20150829182554-ec0926ea948d h1:MFX8DxRnKMY/2M3H61iSsVbo/n3h0MWGmWNN1UViOU0=
|
||||
github.com/koneu/natend v0.0.0-20150829182554-ec0926ea948d/go.mod h1:QHb4k4cr1fQikUahfcRVPcEXiUgFsdIstGqlurL0XL4=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
|
||||
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
|
||||
@ -130,11 +82,10 @@ github.com/kr/pty v1.1.1 h1:VkoXIwSboBpnk99O/KFauAEILuNHv5DVFKZMBN/gUgw=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/krolaw/dhcp4 v0.0.0-20190909130307-a50d88189771 h1:t2c2B9g1ZVhMYduqmANSEGVD3/1WlsrEYNPtVoFlENk=
|
||||
github.com/krolaw/dhcp4 v0.0.0-20190909130307-a50d88189771/go.mod h1:0AqAH3ZogsCrvrtUpvc6EtVKbc3w6xwZhkvGLuqyi3o=
|
||||
github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||
github.com/libdns/cloudflare v0.0.0-20200506154110-16482ae4e806 h1:7T3AeC4TMBBc2kliYawArGb9eWBRrb6gwcSG2JvKDDw=
|
||||
github.com/libdns/cloudflare v0.0.0-20200506154110-16482ae4e806/go.mod h1:A9MqNmkZcd81mY7JsNysmgmj5O9vlRjfDVaNw4j9pjU=
|
||||
github.com/libdns/cloudflare v0.0.0-20200528144945-97886e7873b1 h1:Jx0AoxHtj2NMwxHByM8VmcqvGMa3lEu28xVDArhSi7E=
|
||||
github.com/libdns/cloudflare v0.0.0-20200528144945-97886e7873b1/go.mod h1:A9MqNmkZcd81mY7JsNysmgmj5O9vlRjfDVaNw4j9pjU=
|
||||
github.com/libdns/libdns v0.0.0-20200430163404-ee2c42449104/go.mod h1:yQCXzk1lEZmmCPa857bnk4TsOiqYasqpyOEeSObbb40=
|
||||
@ -144,14 +95,12 @@ github.com/lucasb-eyer/go-colorful v0.0.0-20181028223441-12d3b2882a08/go.mod h1:
|
||||
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
|
||||
github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
||||
github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
|
||||
github.com/mattn/go-runewidth v0.0.7/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
||||
github.com/mdlayher/genetlink v1.0.0 h1:OoHN1OdyEIkScEmRgxLEe2M9U8ClMytqA5niynLtfj0=
|
||||
github.com/mdlayher/genetlink v1.0.0/go.mod h1:0rJ0h4itni50A86M2kHcgS85ttZazNt7a8H2a2cw0Gc=
|
||||
github.com/mdlayher/ndp v0.0.0-20200509194142-8a50b5ef8b52 h1:qWqNvHaKhGECNieU1gGusKRuoPeoR+rhlkaWdO1gyT8=
|
||||
github.com/mdlayher/ndp v0.0.0-20200509194142-8a50b5ef8b52/go.mod h1:AXE3T2f7eg/MV02LS+DGHgH0c+ehknWViE4pgbHtZf8=
|
||||
github.com/mdlayher/netlink v0.0.0-20180912140650-18e318c2e5d1/go.mod h1:a3TlQHkJH2m32RF224Z7LhD5N4mpyR8eUbCoYHywrwg=
|
||||
github.com/mdlayher/netlink v0.0.0-20190409211403-11939a169225/go.mod h1:eQB3mZE4aiYnlUsyGGCOpPETfdQq4Jhsgf1fk3cwQaA=
|
||||
github.com/mdlayher/netlink v0.0.0-20191009155606-de872b0d824b/go.mod h1:KxeJAFOFLG6AjpyDkQ/iIhxygIUKD+vcwqcnu43w/+M=
|
||||
github.com/mdlayher/netlink v1.0.0/go.mod h1:KxeJAFOFLG6AjpyDkQ/iIhxygIUKD+vcwqcnu43w/+M=
|
||||
@ -160,12 +109,6 @@ github.com/mdlayher/netlink v1.1.0/go.mod h1:H4WCitaheIsdF9yOYu8CFmCgQthAPIWZmcK
|
||||
github.com/mdlayher/raw v0.0.0-20190303161257-764d452d77af/go.mod h1:rC/yE65s/DoHB6BzVOUBNYBGTg772JVytyAytffIZkY=
|
||||
github.com/mdlayher/raw v0.0.0-20191009151244-50f2db8cc065 h1:aFkJ6lx4FPip+S+Uw4aTegFMct9shDvP+79PsSxpm3w=
|
||||
github.com/mdlayher/raw v0.0.0-20191009151244-50f2db8cc065/go.mod h1:7EpbotpCmVZcu+KCX4g9WaRNuu11uyhiW7+Le1dKawg=
|
||||
github.com/micro/mdns v0.1.0/go.mod h1:KJ0dW7KmicXU2BV++qkLlmHYcVv7/hHnbtguSWt9Aoc=
|
||||
github.com/miekg/dns v1.1.3/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
|
||||
github.com/miekg/dns v1.1.27 h1:aEH/kqUzUxGJ/UHcEKdJY+ugH6WEzsEBBSPa8zuy1aM=
|
||||
github.com/miekg/dns v1.1.27/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM=
|
||||
github.com/miekg/dns v1.1.28 h1:gQhy5bsJa8zTlVI8lywCTZp1lguor+xevFoYlzeCTQY=
|
||||
github.com/miekg/dns v1.1.28/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM=
|
||||
github.com/miekg/dns v1.1.29 h1:xHBEhR+t5RzcFJjBLJlax2daXOrTYtr9z4WdKEfWFzg=
|
||||
github.com/miekg/dns v1.1.29/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM=
|
||||
github.com/mikioh/ipaddr v0.0.0-20190404000644-d465c8ab6721 h1:RlZweED6sbSArvlE924+mUcZuXKLBHA35U7LN621Bws=
|
||||
@ -175,84 +118,35 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJ
|
||||
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||
github.com/olekukonko/tablewriter v0.0.4/go.mod h1:zq6QwlOf5SlnkVbMSr5EoBv3636FWnp+qbPhuoO21uA=
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
||||
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
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/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
|
||||
github.com/prometheus/client_golang v0.9.2/go.mod h1:OsXs2jCmiKlQ1lTBmv21f2mNfw4xf/QclQDMrYNZzcM=
|
||||
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
|
||||
github.com/prometheus/client_golang v1.2.1/go.mod h1:XMU6Z2MjaRKVu/dC1qupJI9SiNkDYzz3xecMgSW/F+U=
|
||||
github.com/prometheus/client_golang v1.4.1 h1:FFSuS004yOQEtDdTq+TAOLP5xUq63KqAFYyOi8zA+Y8=
|
||||
github.com/prometheus/client_golang v1.4.1/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU=
|
||||
github.com/prometheus/client_golang v1.5.1 h1:bdHYieyGlH+6OLEk2YQha8THib30KP0/yD0YH9m6xcA=
|
||||
github.com/prometheus/client_golang v1.5.1/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU=
|
||||
github.com/prometheus/client_golang v1.6.0 h1:YVPodQOcK15POxhgARIvnDRVpLcuK8mglnMrWfyrw6A=
|
||||
github.com/prometheus/client_golang v1.6.0/go.mod h1:ZLOG9ck3JLRdB5MgO8f+lLTe83AXG6ro35rLTxvnIl4=
|
||||
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
|
||||
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M=
|
||||
github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/prometheus/common v0.0.0-20181126121408-4724e9255275/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
|
||||
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
|
||||
github.com/prometheus/common v0.7.0/go.mod h1:DjGbpBbp5NYNiECxcL/VnbXCCaQpKd3tt26CguLLsqA=
|
||||
github.com/prometheus/common v0.9.1 h1:KOMtN28tlbam3/7ZKEYKHhKoJZYYj3gMH4uc62x7X7U=
|
||||
github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4=
|
||||
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||
github.com/prometheus/procfs v0.0.0-20181204211112-1dc9a6cbc91a/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
|
||||
github.com/prometheus/procfs v0.0.5/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ=
|
||||
github.com/prometheus/procfs v0.0.8 h1:+fpWZdT24pJBiqJdAwYBjPSk+5YmQzYNPYzQsdzLkt8=
|
||||
github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A=
|
||||
github.com/prometheus/procfs v0.0.10 h1:QJQN3jYQhkamO4mhfUWqdDH2asK7ONOI9MTWjyAxNKM=
|
||||
github.com/prometheus/procfs v0.0.10/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A=
|
||||
github.com/prometheus/procfs v0.0.11 h1:DhHlBtkHWPYi8O2y31JkK0TF+DGM+51OopZjH/Ia5qI=
|
||||
github.com/prometheus/procfs v0.0.11/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
|
||||
github.com/rivo/tview v0.0.0-20181226202439-36893a669792/go.mod h1:J4W+hErFfITUbyFAEXizpmkuxX7ZN56dopxHB4XQhMw=
|
||||
github.com/rtr7/dhcp4 v0.0.0-20181120124042-778e8c2e24a5 h1:/kzTBQ20DbbhSNaBXiFEk2gPrGhY26kajwC1ro/Vlh8=
|
||||
github.com/rtr7/dhcp4 v0.0.0-20181120124042-778e8c2e24a5/go.mod h1:FwstIpm6vX98QgtR8KEwZcVjiRn2WP76LjXAHj84fK0=
|
||||
github.com/rtr7/dyndns v0.0.0-20190106135032-c057db8a5daf h1:0E99MScKJIj5KssYbm81k8SzgjZ+4W8rLDQ0fo3kK3Q=
|
||||
github.com/rtr7/dyndns v0.0.0-20190106135032-c057db8a5daf/go.mod h1:nLgRjLaFQWVema6EgYlNQEDSiCi7KjjeZdi9pfJndG0=
|
||||
github.com/rtr7/kernel v0.0.0-20200312144756-7ff5adfbf3ac h1:lq31XGORKW/U472r8q61t1ZVPNqH4pedXujZiZvqzxA=
|
||||
github.com/rtr7/kernel v0.0.0-20200312144756-7ff5adfbf3ac/go.mod h1:4SV8RGZNMM8vVOETMSgMjzMx0jpXR/9H/uiliSJ4S7o=
|
||||
github.com/rtr7/kernel v0.0.0-20200318145025-a61625558534 h1:gcbTKxP7QAwwbByFlujkxrfCMsjjb0uTyuQMP8eGk8I=
|
||||
github.com/rtr7/kernel v0.0.0-20200318145025-a61625558534/go.mod h1:4SV8RGZNMM8vVOETMSgMjzMx0jpXR/9H/uiliSJ4S7o=
|
||||
github.com/rtr7/kernel v0.0.0-20200321215136-8b07a884b803 h1:/HdyvL8vJJdakCaNKX9bczR5V1wppkZc9KiBXKKHOkI=
|
||||
github.com/rtr7/kernel v0.0.0-20200321215136-8b07a884b803/go.mod h1:4SV8RGZNMM8vVOETMSgMjzMx0jpXR/9H/uiliSJ4S7o=
|
||||
github.com/rtr7/kernel v0.0.0-20200325145407-a75b07b98b31 h1:HXMIeEeMVdNPfdIUbWakzZUni/utCtTsc6cOl5uRKLg=
|
||||
github.com/rtr7/kernel v0.0.0-20200325145407-a75b07b98b31/go.mod h1:4SV8RGZNMM8vVOETMSgMjzMx0jpXR/9H/uiliSJ4S7o=
|
||||
github.com/rtr7/kernel v0.0.0-20200326145340-9d8a6f1c8009 h1:Gz/LKq98c+sDmNwbWq0EzphorvvWekRLBnGb1UXLf6Y=
|
||||
github.com/rtr7/kernel v0.0.0-20200326145340-9d8a6f1c8009/go.mod h1:4SV8RGZNMM8vVOETMSgMjzMx0jpXR/9H/uiliSJ4S7o=
|
||||
github.com/rtr7/kernel v0.0.0-20200331065641-2e2aec2883e0 h1:vss4FwTge9tw1SdTDXAvZKcnLEXrOMD1h4qTgVfpwYM=
|
||||
github.com/rtr7/kernel v0.0.0-20200331065641-2e2aec2883e0/go.mod h1:4SV8RGZNMM8vVOETMSgMjzMx0jpXR/9H/uiliSJ4S7o=
|
||||
github.com/rtr7/kernel v0.0.0-20200402145611-2b3923c4fe0e h1:zAjvFoTdlqbeY1cDXPcnGAVhGjD5bH8Ra1Z09a/tcmo=
|
||||
github.com/rtr7/kernel v0.0.0-20200402145611-2b3923c4fe0e/go.mod h1:4SV8RGZNMM8vVOETMSgMjzMx0jpXR/9H/uiliSJ4S7o=
|
||||
github.com/rtr7/kernel v0.0.0-20200408155317-3032e2e727c3 h1:RCoEGnv6k9G0Mhc7RPyk1LZ/THFh9gE+LI1sxBZT9xs=
|
||||
github.com/rtr7/kernel v0.0.0-20200408155317-3032e2e727c3/go.mod h1:4SV8RGZNMM8vVOETMSgMjzMx0jpXR/9H/uiliSJ4S7o=
|
||||
github.com/rtr7/kernel v0.0.0-20200413150315-d7a4ba2cd17b h1:f80pv6wAeq+D/q2fTI8mIDOylsLTlctoYzIQNcrkaYQ=
|
||||
github.com/rtr7/kernel v0.0.0-20200413150315-d7a4ba2cd17b/go.mod h1:4SV8RGZNMM8vVOETMSgMjzMx0jpXR/9H/uiliSJ4S7o=
|
||||
github.com/rtr7/kernel v0.0.0-20200417150634-e4f4a928c71b h1:/jxVXf9gv3ttD9GlF8UvlAcw9i3AaTOiTmPgUx7KrCM=
|
||||
github.com/rtr7/kernel v0.0.0-20200417150634-e4f4a928c71b/go.mod h1:4SV8RGZNMM8vVOETMSgMjzMx0jpXR/9H/uiliSJ4S7o=
|
||||
github.com/rtr7/kernel v0.0.0-20200421150800-28e4edfac793 h1:dOOkFH7Sxww/ENCuVVa+yOz+19o8lOMrGzc7jBe+xHg=
|
||||
github.com/rtr7/kernel v0.0.0-20200421150800-28e4edfac793/go.mod h1:4SV8RGZNMM8vVOETMSgMjzMx0jpXR/9H/uiliSJ4S7o=
|
||||
github.com/rtr7/kernel v0.0.0-20200423150953-b7c726df7032 h1:xhP9F/Eh05B1IJRsYpM5j05s/qUnIWh0iEx5xj19/7g=
|
||||
github.com/rtr7/kernel v0.0.0-20200423150953-b7c726df7032/go.mod h1:4SV8RGZNMM8vVOETMSgMjzMx0jpXR/9H/uiliSJ4S7o=
|
||||
github.com/rtr7/kernel v0.0.0-20200503151124-7254f9a96398 h1:blIgOC9XdboUb3VEnoQFVjyxpPwZ7CWqxBc2TzVm9bo=
|
||||
github.com/rtr7/kernel v0.0.0-20200503151124-7254f9a96398/go.mod h1:4SV8RGZNMM8vVOETMSgMjzMx0jpXR/9H/uiliSJ4S7o=
|
||||
github.com/rtr7/kernel v0.0.0-20200506151338-66f9d4444856 h1:ZI8DnPme5d8jU6Ge4yBZ0BIPYJIEod86iiPQhMaFNzw=
|
||||
github.com/rtr7/kernel v0.0.0-20200506151338-66f9d4444856/go.mod h1:4SV8RGZNMM8vVOETMSgMjzMx0jpXR/9H/uiliSJ4S7o=
|
||||
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0=
|
||||
github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
|
||||
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
||||
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
||||
github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4=
|
||||
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
||||
github.com/stapelberg/zkj-nas-tools v0.0.0-20200309084414-3f5eb432164d h1:xcHcJaUvrMf5lednLxBTUH0V8eE3J+lCIBCdupax8AI=
|
||||
github.com/stapelberg/zkj-nas-tools v0.0.0-20200309084414-3f5eb432164d/go.mod h1:naxJPyny33hbnsg6bBg+XQCLo1DLzri9abEjy0aoKuU=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.1.1 h1:2vfRuCMp5sSVIDSqO8oNnWJq7mPa6KVP3iPIwFBuy8A=
|
||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
@ -260,15 +154,10 @@ github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXf
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4=
|
||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||
github.com/tdewolff/parse v2.3.4+incompatible/go.mod h1:8oBwCsVmUkgHO8M5iCzSIDtpzXOT0WXX9cWhz+bIzJQ=
|
||||
github.com/twpayne/go-kml v1.2.0/go.mod h1:LlvLIQSfMqYk2O7Nx8vYAbSLv4K9rjMvLlEdUKWdjq0=
|
||||
github.com/twpayne/go-polyline v1.0.0/go.mod h1:ICh24bcLYBX8CknfvNPKqoTbe+eg+MX1NPyJmSBo7pU=
|
||||
github.com/u-root/u-root v6.0.0+incompatible h1:YqPGmRoRyYmeg17KIWFRSyVq6LX5T6GSzawyA6wG6EE=
|
||||
github.com/u-root/u-root v6.0.0+incompatible/go.mod h1:RYkpo8pTHrNjW08opNd/U6p/RJE7K0D8fXO0d47+3YY=
|
||||
github.com/urfave/cli v1.22.3/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
|
||||
github.com/urfave/cli/v2 v2.1.1/go.mod h1:SE9GqnLQmjVa0iPEY0f1w3ygNIYcIJ0OKPMoW2caLfQ=
|
||||
github.com/vishvananda/netlink v1.0.0/go.mod h1:+SR5DhBJrl6ZM7CoCKvpw5BKroDKQ+PJqOg65H/2ktk=
|
||||
github.com/vishvananda/netlink v1.1.0 h1:1iyaYNBLmP6L0220aDnYQpo1QEV4t4hJ+xEEhhJH8j0=
|
||||
github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE=
|
||||
github.com/vishvananda/netns v0.0.0-20180720170159-13995c7128cc/go.mod h1:ZjcWmFBXmLKZu9Nxj3WKYEafiSqer2rnvPr0en9UNpI=
|
||||
@ -277,42 +166,16 @@ github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17
|
||||
gitlab.com/golang-commonmark/puny v0.0.0-20191124015043-9f83538fa04f h1:Wku8eEdeJqIOFHtrfkYUByc4bCaTeA6fL0UJgfEiFMI=
|
||||
gitlab.com/golang-commonmark/puny v0.0.0-20191124015043-9f83538fa04f/go.mod h1:Tiuhl+njh/JIg0uS/sOJVYi0x2HEa5rc1OAaVsb5tAs=
|
||||
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20190130090550-b01c7a725664/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20191002192127-34f69633bfdc/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20200204104054-c9f3fb736b72/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20200214034016-1d94cc7ab1c6 h1:Sy5bstxEqwwbYs6n0/pBuxKENqOeZUgD45Gp3Q3pqLg=
|
||||
golang.org/x/crypto v0.0.0-20200214034016-1d94cc7ab1c6/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20200317142112-1b76d66859c6 h1:TjszyFsQsyZNHwdVdZ5m7bjmreu0znc2kRYsEml9/Ww=
|
||||
golang.org/x/crypto v0.0.0-20200317142112-1b76d66859c6/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20200320181102-891825fb96df h1:lDWgvUvNnaTnNBc/dwOty86cFeKoKWbwy2wQj0gIxbU=
|
||||
golang.org/x/crypto v0.0.0-20200320181102-891825fb96df/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59 h1:3zb4D3T4G8jdExgVU/95+vQXfpEPiMdCaZgmGVxjNHM=
|
||||
golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20200403201458-baeed622b8d8 h1:fpnn/HnJONpIu6hkXi1u/7rR0NzilgWr4T0JmWkEitk=
|
||||
golang.org/x/crypto v0.0.0-20200403201458-baeed622b8d8/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20200406173513-056763e48d71 h1:DOmugCavvUtnUD114C1Wh+UgTgQZ4pMLzXxi1pSt+/Y=
|
||||
golang.org/x/crypto v0.0.0-20200406173513-056763e48d71/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20200414173820-0848c9571904 h1:bXoxMPcSLOq08zI3/c5dEBT6lE4eh+jOh886GHrn6V8=
|
||||
golang.org/x/crypto v0.0.0-20200414173820-0848c9571904/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20200420201142-3c4aac89819a h1:y6sBfNd1b9Wy08a6K1Z1DZc4aXABUN5TKjkYhz7UKmo=
|
||||
golang.org/x/crypto v0.0.0-20200420201142-3c4aac89819a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20200422194213-44a606286825 h1:dSChiwOTvzwbHFTMq2l6uRardHH7/E6SqEkqccinS/o=
|
||||
golang.org/x/crypto v0.0.0-20200422194213-44a606286825/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20200423211502-4bdfaf469ed5 h1:Q7tZBpemrlsc2I7IyODzhtallWRSm4Q0d09pL6XbQtU=
|
||||
golang.org/x/crypto v0.0.0-20200423211502-4bdfaf469ed5/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20200427165652-729f1e841bcc h1:ZGI/fILM2+ueot/UixBSoj9188jCAxVHEZEGhqq67I4=
|
||||
golang.org/x/crypto v0.0.0-20200427165652-729f1e841bcc/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20200429183012-4b2356b1ed79 h1:IaQbIIB2X/Mp/DKctl6ROxz1KyMlKp4uyvL6+kQ7C88=
|
||||
golang.org/x/crypto v0.0.0-20200429183012-4b2356b1ed79/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||
golang.org/x/net v0.0.0-20170809000501-1c05540f6879/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190419010253-1f3472d942ba/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
@ -325,22 +188,6 @@ golang.org/x/net v0.0.0-20191007182048-72f939374954/go.mod h1:z5CRVTTTmAJ677TzLL
|
||||
golang.org/x/net v0.0.0-20191028085509-fe3aa8a45271/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200202094626-16171245cfb2 h1:CCH4IOTTfewWjGOlSp+zGcjutRKlBEZQ6wTn8ozI/nI=
|
||||
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200301022130-244492dfa37a h1:GuSPYbZzB5/dcLNCwLQLsg3obCJtX9IJhpXkvY7kzk0=
|
||||
golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200319234117-63522dbf7eec h1:w0SItUiQ4sBiXBAwWNkyu8Fu2Qpn/dtDIcoPkPDqjRw=
|
||||
golang.org/x/net v0.0.0-20200319234117-63522dbf7eec/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200320220750-118fecf932d8 h1:1+zQlQqEEhUeStBTi653GZAnAuivZq/2hz+Iz+OP7rg=
|
||||
golang.org/x/net v0.0.0-20200320220750-118fecf932d8/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e h1:3G+cUijn7XD+S4eJFddp53Pv7+slrESplyjG25HgL+k=
|
||||
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20200421231249-e086a090c8fd h1:QPwSajcTUrFriMF1nJ3XzgoqakqQEsnZf9LdXdi2nkI=
|
||||
golang.org/x/net v0.0.0-20200421231249-e086a090c8fd/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20200425230154-ff2c4b7c35a0 h1:Jcxah/M+oLZ/R4/z5RzfPzGbPXnVDPkEDtf2JnuxN+U=
|
||||
golang.org/x/net v0.0.0-20200425230154-ff2c4b7c35a0/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5 h1:WQ8q63x+f/zpC8Ac1s9wLElVoHhm32p6tudrU72n1QA=
|
||||
golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20200505041828-1ed23360d12c h1:zJ0mtu4jCalhKg6Oaukv6iIkb+cOvDrajDH9DH46Q4M=
|
||||
golang.org/x/net v0.0.0-20200505041828-1ed23360d12c/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20200506145744-7e3656a0809f h1:QBjCr1Fz5kw158VqdE9JfI9cJnl/ymnJWAdMuinqL7Y=
|
||||
golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
@ -350,11 +197,9 @@ golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e h1:vcxGaoTs7kV8m5Np9uUNQin4
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a h1:WXEvlFVvvGxCJLG6REjsT03iWnKLEWinaScsxF2Vm2o=
|
||||
golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20170809190605-e42485b6e20a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181228144115-9a3f9b0469bb/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190129075346-302c3dd5f1cc/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-20190405154228-4b34438f7a67/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
@ -367,45 +212,21 @@ golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191003212358-c178f38b412c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191010194322-b09406accb47/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191029155521-f43be2a4598c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4 h1:sfkvUWPNGwSV+8/fNqctR5lS2AqCSqYwXdrjCxp/dXo=
|
||||
golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200317113312-5766fd39f98d h1:62ap6LNOjDU6uGmKXHJbSfciMoV+FeI1sRXx/pLDL44=
|
||||
golang.org/x/sys v0.0.0-20200317113312-5766fd39f98d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200321134203-328b4cd54aae h1:3tcmuaB7wwSZtelmiv479UjUB+vviwABz7a133ZwOKQ=
|
||||
golang.org/x/sys v0.0.0-20200321134203-328b4cd54aae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd h1:xhmwyvizuTgC2qz7ZlMluP20uW+C3Rm0FD/WLDX8884=
|
||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200327173247-9dae0f8f5775 h1:TC0v2RSO1u2kn1ZugjrFXkRZAEaqMN/RW+OTZkBzmLE=
|
||||
golang.org/x/sys v0.0.0-20200327173247-9dae0f8f5775/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200331124033-c3d80250170d h1:nc5K6ox/4lTFbMVSL9WRR81ixkcwXThoiF6yf+R9scA=
|
||||
golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200406155108-e3b113bbe6a4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200408040146-ea54a3c99b9b h1:h03Ur1RlPrGTjua4koYdpGl8W0eYo8p1uI9w7RPlkdk=
|
||||
golang.org/x/sys v0.0.0-20200408040146-ea54a3c99b9b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200413165638-669c56c373c4 h1:opSr2sbRXk5X5/givKrrKj9HXxFpW2sdCiP8MJSKLQY=
|
||||
golang.org/x/sys v0.0.0-20200413165638-669c56c373c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200420163511-1957bb5e6d1f h1:gWF768j/LaZugp8dyS4UwsslYCYz9XgFxvlgsn0n9H8=
|
||||
golang.org/x/sys v0.0.0-20200420163511-1957bb5e6d1f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200427175716-29b57079015a h1:08u6b1caTT9MQY4wSbmsd4Ulm6DmgNYnbImBuZjGJow=
|
||||
golang.org/x/sys v0.0.0-20200427175716-29b57079015a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200428200454-593003d681fa h1:yMbJOvnfYkO1dSAviTu/ZguZWLBTXx4xE3LYrxUCCiA=
|
||||
golang.org/x/sys v0.0.0-20200428200454-593003d681fa/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200501145240-bc7a7d42d5c3 h1:5B6i6EAiSYyejWfvc5Rc9BbI3rzIsrrXfAQBWnYfn+w=
|
||||
golang.org/x/sys v0.0.0-20200501145240-bc7a7d42d5c3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200509044756-6aff5f38e54f h1:mOhmO9WsBaJCNmaZHPtHs9wOcdqdKCjF6OPJlmDM3KI=
|
||||
golang.org/x/sys v0.0.0-20200509044756-6aff5f38e54f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0 h1:/5xXl8Y5W96D+TtHSlonuFqGHIWVuyCkGJLwGh9JJFs=
|
||||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1 h1:NusfzzA6yGQ+ua51ck7E3omNUX/JuqbFSaRGqU8CcLI=
|
||||
golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
@ -417,8 +238,6 @@ golang.zx2c4.com/wireguard v0.0.20200121 h1:vcswa5Q6f+sylDfjqyrVNNrjsFUUbPsgAQTB
|
||||
golang.zx2c4.com/wireguard v0.0.20200121/go.mod h1:P2HsVp8SKwZEufsnezXZA4GRX/T49/HlU7DGuelXsU4=
|
||||
golang.zx2c4.com/wireguard v0.0.20200320 h1:1vE6zVeO7fix9cJX1Z9ZQ+ikPIIx7vIyU0o0tLDD88g=
|
||||
golang.zx2c4.com/wireguard v0.0.20200320/go.mod h1:lDian4Sw4poJ04SgHh35nzMVwGSYlPumkdnHcucAQoY=
|
||||
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20200205215550-e35592f146e4 h1:KTi97NIQGgSMaN0v/oxniJV0MEzfzmrDUOAWxombQVc=
|
||||
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20200205215550-e35592f146e4/go.mod h1:UdS9frhv65KTfwxME1xE8+rHYoFpbm36gOud1GhBe9c=
|
||||
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20200324154536-ceff61240acf h1:rWUZHukj3poXegPQMZOXgxjTGIBe3mLNHNVvL5DsHus=
|
||||
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20200324154536-ceff61240acf/go.mod h1:UdS9frhv65KTfwxME1xE8+rHYoFpbm36gOud1GhBe9c=
|
||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||
@ -434,9 +253,13 @@ gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLks
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
|
||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/cheggaaa/pb.v1 v1.0.27/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw=
|
||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.5 h1:ymVxjfMaHvXD8RqPRmzHHsB3VvucivSkIAvJFDI5O3c=
|
||||
gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
|
||||
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
|
51
init/init.go
Normal file
51
init/init.go
Normal file
@ -0,0 +1,51 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"log"
|
||||
"os/exec"
|
||||
"path"
|
||||
|
||||
"github.com/gokrazy/gokrazy"
|
||||
)
|
||||
|
||||
// buildTimestamp can be overridden by specifying e.g.
|
||||
// -ldflags "-X main.buildTimestamp=foo" when building.
|
||||
var (
|
||||
buildTimestamp = "2020-06-08T19:45:52-07:00"
|
||||
|
||||
domain string
|
||||
cmdRoot string
|
||||
perm string
|
||||
noFirewall bool
|
||||
)
|
||||
|
||||
func main() {
|
||||
flag.StringVar(&cmdRoot, "cmdroot", "/usr/bin", "path to rtr7 binaries")
|
||||
flag.StringVar(&domain, "domain", "lan", "domain name for your network")
|
||||
flag.StringVar(&perm, "perm", "/var/lib/rtr7/", "path to replace /perm")
|
||||
flag.BoolVar(&noFirewall, "nofirewall", false, "disable the rtr7 firewall")
|
||||
flag.Parse()
|
||||
log.SetFlags(log.LstdFlags | log.Lshortfile)
|
||||
|
||||
fmt.Printf("gokrazy build timestamp %s\n", buildTimestamp)
|
||||
|
||||
cmds := []*exec.Cmd{
|
||||
// exec.Command(path.Join(cmdRoot, "/ntp")),
|
||||
exec.Command(path.Join(cmdRoot, "backupd"), "-perm="+perm),
|
||||
exec.Command(path.Join(cmdRoot, "captured"), "-perm="+perm),
|
||||
exec.Command(path.Join(cmdRoot, "dhcp4"), "-perm="+perm),
|
||||
exec.Command(path.Join(cmdRoot, "dhcp4d"), "-perm="+perm),
|
||||
exec.Command(path.Join(cmdRoot, "dhcp6"), "-perm="+perm),
|
||||
exec.Command(path.Join(cmdRoot, "diagd"), "-perm="+perm),
|
||||
exec.Command(path.Join(cmdRoot, "dnsd"), fmt.Sprintf("-domain=%s", domain), "-perm="+perm),
|
||||
exec.Command(path.Join(cmdRoot, "dyndns"), "-perm="+perm),
|
||||
exec.Command(path.Join(cmdRoot, "netconfigd"), fmt.Sprintf("-nofirewall=%t", noFirewall), "-perm="+perm),
|
||||
exec.Command(path.Join(cmdRoot, "radvd"), "-perm="+perm),
|
||||
}
|
||||
if err := gokrazy.Supervise(cmds); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
select {}
|
||||
}
|
@ -18,9 +18,12 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"path"
|
||||
"time"
|
||||
)
|
||||
|
||||
var Perm = "/perm"
|
||||
|
||||
func leaseValid(fn string) (status string, _ error) {
|
||||
var lease struct {
|
||||
ValidUntil time.Time `json:"valid_until"`
|
||||
@ -56,7 +59,7 @@ func (d *dhcpv4) Children() []Node {
|
||||
}
|
||||
|
||||
func (d *dhcpv4) Evaluate() (string, error) {
|
||||
return leaseValid("/perm/dhcp4/wire/lease.json")
|
||||
return leaseValid(path.Join(Perm, "/dhcp4/wire/lease.json"))
|
||||
}
|
||||
|
||||
// DHCPv4 returns a Node which succeeds if /perm/dhcp4/wire/lease.json contains
|
||||
@ -83,7 +86,7 @@ func (d *dhcpv6) Children() []Node {
|
||||
}
|
||||
|
||||
func (d *dhcpv6) Evaluate() (string, error) {
|
||||
return leaseValid("/perm/dhcp6/wire/lease.json")
|
||||
return leaseValid(path.Join(Perm, "/dhcp6/wire/lease.json"))
|
||||
}
|
||||
|
||||
// DHCPv6 returns a Node which succeeds if /perm/dhcp6/wire/lease.json contains
|
||||
|
@ -16,6 +16,7 @@
|
||||
package dns
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math"
|
||||
@ -42,11 +43,17 @@ var log = teelogger.NewConsole()
|
||||
// DHCP-based local name resolution can be made case-insensitive.
|
||||
type lcHostname string
|
||||
|
||||
type Server struct {
|
||||
Mux *dns.ServeMux
|
||||
type IP struct {
|
||||
IPv6 net.IP `json:"ipv6"`
|
||||
IPv4 net.IP `json:"ipv4"`
|
||||
Host lcHostname `json:"host"` // lease that the IPs are updated from. If no lease exists for this host it is never updated.
|
||||
}
|
||||
|
||||
type Server struct {
|
||||
Mux *dns.ServeMux
|
||||
once bool
|
||||
client *dns.Client
|
||||
domain string
|
||||
domain lcHostname
|
||||
sometimes *rate.Limiter
|
||||
prom struct {
|
||||
registry *prometheus.Registry
|
||||
@ -59,21 +66,34 @@ type Server struct {
|
||||
hostname, ip string
|
||||
hostsByName map[lcHostname]string
|
||||
hostsByIP map[string]string
|
||||
subnames map[lcHostname]map[string]net.IP // hostname → subname → ip
|
||||
subnames map[lcHostname]map[lcHostname]IP // hostname → subname → ip
|
||||
|
||||
upstreamMu sync.RWMutex
|
||||
upstream []string
|
||||
}
|
||||
|
||||
func (lh *lcHostname) UnmarshalJSON(b []byte) error {
|
||||
var s string
|
||||
if err := json.Unmarshal(b, &s); err != nil {
|
||||
return err
|
||||
}
|
||||
*lh = lcHostname(strings.ToLower(s))
|
||||
return nil
|
||||
}
|
||||
|
||||
func NewServer(addr, domain string) *Server {
|
||||
hostname, _ := os.Hostname()
|
||||
ip, _, _ := net.SplitHostPort(addr)
|
||||
server := &Server{
|
||||
Mux: dns.NewServeMux(),
|
||||
client: &dns.Client{},
|
||||
domain: domain,
|
||||
domain: lcHostname(strings.ToLower(domain)),
|
||||
upstream: []string{
|
||||
// https://developers.google.com/speed/public-dns/docs/using#google_public_dns_ip_addresses
|
||||
"1.1.1.1:53",
|
||||
"1.0.0.1:53",
|
||||
"2606:4700:4700::1111:53",
|
||||
"2606:4700:4700::1001:53",
|
||||
"8.8.8.8:53",
|
||||
"8.8.4.4:53",
|
||||
"[2001:4860:4860::8888]:53",
|
||||
@ -82,7 +102,7 @@ func NewServer(addr, domain string) *Server {
|
||||
sometimes: rate.NewLimiter(rate.Every(1*time.Second), 1), // at most once per second
|
||||
hostname: hostname,
|
||||
ip: ip,
|
||||
subnames: make(map[lcHostname]map[string]net.IP),
|
||||
subnames: make(map[lcHostname]map[lcHostname]IP),
|
||||
}
|
||||
server.prom.registry = prometheus.NewRegistry()
|
||||
|
||||
@ -111,7 +131,8 @@ func NewServer(addr, domain string) *Server {
|
||||
server.prom.registry.MustRegister(prometheus.NewGoCollector())
|
||||
server.initHostsLocked()
|
||||
server.Mux.HandleFunc(".", server.handleRequest)
|
||||
server.Mux.HandleFunc("lan.", server.handleInternal)
|
||||
server.Mux.HandleFunc(strings.ToLower(domain)+".", server.subnameHandler(server.domain))
|
||||
server.Mux.HandleFunc("lan.", server.subnameHandler(server.domain))
|
||||
server.Mux.HandleFunc("localhost.", server.handleInternal)
|
||||
go func() {
|
||||
for range time.Tick(10 * time.Second) {
|
||||
@ -124,14 +145,20 @@ func NewServer(addr, domain string) *Server {
|
||||
func (s *Server) initHostsLocked() {
|
||||
s.hostsByName = make(map[lcHostname]string)
|
||||
s.hostsByIP = make(map[string]string)
|
||||
s.subnames[s.domain] = make(map[lcHostname]IP)
|
||||
if s.hostname != "" && s.ip != "" {
|
||||
lower := strings.ToLower(s.hostname)
|
||||
s.hostsByName[lcHostname(lower)] = s.ip
|
||||
lower := lcHostname(strings.ToLower(s.hostname))
|
||||
s.hostsByName[lower] = s.ip
|
||||
if rev, err := dns.ReverseAddr(s.ip); err == nil {
|
||||
s.hostsByIP[rev] = s.hostname
|
||||
}
|
||||
s.Mux.HandleFunc(lower+".", s.subnameHandler(s.hostname))
|
||||
s.Mux.HandleFunc(lower+"."+s.domain+".", s.subnameHandler(s.hostname))
|
||||
subnames := s.subnames[s.domain]
|
||||
ip := net.ParseIP(s.ip)
|
||||
if ip.To4() != nil {
|
||||
subnames[lower] = IP{IPv4: ip}
|
||||
} else {
|
||||
subnames[lower] = IP{IPv6: ip}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -181,10 +208,10 @@ func (s *Server) probeUpstreamLatency() {
|
||||
s.upstream = upstreams
|
||||
}
|
||||
|
||||
func (s *Server) hostByName(n string) (string, bool) {
|
||||
func (s *Server) hostByName(n lcHostname) (string, bool) {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
r, ok := s.hostsByName[lcHostname(strings.ToLower(n))]
|
||||
r, ok := s.hostsByName[n]
|
||||
return r, ok
|
||||
}
|
||||
|
||||
@ -195,53 +222,128 @@ func (s *Server) hostByIP(n string) (string, bool) {
|
||||
return r, ok
|
||||
}
|
||||
|
||||
func (s *Server) subname(hostname, host string) (net.IP, bool) {
|
||||
func (s *Server) subname(hostname, host string) (IP, bool) {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
r, ok := s.subnames[lcHostname(strings.ToLower(hostname))][host]
|
||||
r, ok := s.subnames[lcHostname(strings.ToLower(hostname))][lcHostname(strings.ToLower(host))]
|
||||
return r, ok
|
||||
}
|
||||
|
||||
func (s *Server) setSubname(ip IP) {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
hdnSlice := strings.SplitN(string(ip.Host), ".", 2)
|
||||
host := lcHostname(hdnSlice[0])
|
||||
domain := lcHostname("")
|
||||
if len(hdnSlice) == 2 {
|
||||
domain = lcHostname(hdnSlice[1])
|
||||
}
|
||||
if domain == "" {
|
||||
domain = s.domain
|
||||
}
|
||||
subnames, ok := s.subnames[domain]
|
||||
if !ok {
|
||||
subnames = make(map[lcHostname]IP)
|
||||
s.subnames[domain] = subnames
|
||||
}
|
||||
curIP, ok := subnames[host]
|
||||
if !ok {
|
||||
subnames[host] = ip
|
||||
} else {
|
||||
// refuse to overwrite a lease
|
||||
if _, ok := s.hostsByName[ip.Host]; ok {
|
||||
if curIP.IPv4 == nil {
|
||||
curIP.IPv4 = ip.IPv4
|
||||
}
|
||||
if curIP.IPv6 == nil {
|
||||
curIP.IPv6 = ip.IPv6
|
||||
}
|
||||
subnames[host] = curIP
|
||||
} else {
|
||||
subnames[host] = ip
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Server) PrometheusHandler() http.Handler {
|
||||
return promhttp.HandlerFor(s.prom.registry, promhttp.HandlerOpts{})
|
||||
}
|
||||
|
||||
func (s *Server) DyndnsHandler(w http.ResponseWriter, r *http.Request) {
|
||||
host := r.FormValue("host")
|
||||
var (
|
||||
hostname lcHostname // with domain
|
||||
hostlan string // with lan domain
|
||||
)
|
||||
host := strings.Trim(r.FormValue("host"), ". ")
|
||||
ip := net.ParseIP(r.FormValue("ip"))
|
||||
if ip == nil {
|
||||
http.Error(w, "invalid ip", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
remote, _, err := net.SplitHostPort(r.RemoteAddr)
|
||||
if err != nil {
|
||||
http.Error(w, fmt.Sprintf("net.SplitHostPort(%q): %v", r.RemoteAddr, err), http.StatusBadRequest)
|
||||
// s.mu.Lock()
|
||||
// defer s.mu.Unlock()
|
||||
/*
|
||||
remote, _, err := net.SplitHostPort(r.RemoteAddr)
|
||||
if err != nil {
|
||||
http.Error(w, fmt.Sprintf("net.SplitHostPort(%q): %v", r.RemoteAddr, err), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
rev, err := dns.ReverseAddr(remote)
|
||||
if err != nil {
|
||||
http.Error(w, fmt.Sprintf("dns.ReverseAddr(%v): %v", remote, err), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
hostname, ok := s.hostsByIP[rev]
|
||||
if !ok {
|
||||
err := fmt.Sprintf("connection without corresponding DHCP lease: %v", rev)
|
||||
http.Error(w, err, http.StatusForbidden)
|
||||
return
|
||||
}
|
||||
*/
|
||||
if strings.HasSuffix(host, "localhost") {
|
||||
http.Error(w, fmt.Sprintf("invalid localhost not allowed: %v", host), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
rev, err := dns.ReverseAddr(remote)
|
||||
if err != nil {
|
||||
http.Error(w, fmt.Sprintf("dns.ReverseAddr(%v): %v", remote, err), http.StatusBadRequest)
|
||||
return
|
||||
hostname = lcHostname(strings.ToLower(host))
|
||||
if strings.HasSuffix(string(hostname), ".lan") { // change lan to domain
|
||||
hostname = lcHostname(strings.TrimSuffix(string(hostname), "lan")) + s.domain
|
||||
} else if !strings.HasSuffix(string(hostname), "."+string(s.domain)) { // add domain if not already there
|
||||
hostname += "." + s.domain
|
||||
}
|
||||
hostname, ok := s.hostsByIP[rev]
|
||||
if !ok {
|
||||
err := fmt.Sprintf("connection without corresponding DHCP lease: %v", rev)
|
||||
http.Error(w, err, http.StatusForbidden)
|
||||
return
|
||||
|
||||
hostlan = strings.TrimSuffix(string(hostname), string(s.domain)) + "lan"
|
||||
|
||||
ipr := IP{
|
||||
Host: hostname,
|
||||
}
|
||||
lower := strings.ToLower(hostname)
|
||||
subnames, ok := s.subnames[lcHostname(lower)]
|
||||
if !ok {
|
||||
subnames = make(map[string]net.IP)
|
||||
s.subnames[lcHostname(lower)] = subnames
|
||||
if ip.To4() == nil {
|
||||
ipr.IPv6 = ip
|
||||
} else {
|
||||
ipr.IPv4 = ip
|
||||
}
|
||||
s.setSubname(ipr)
|
||||
if strings.Contains(strings.TrimSuffix(string(ipr.Host), "."+string(s.domain)), ".") { // strip domain if it still has a "." it is a subname
|
||||
hdnSlice := strings.SplitN(string(ipr.Host), ".", 2)
|
||||
domain := lcHostname(hdnSlice[1]) // guaranteed by if statement
|
||||
|
||||
s.Mux.HandleFunc(strings.ToLower(host), s.subnameHandler(domain)) // from post
|
||||
s.Mux.HandleFunc(string(hostname), s.subnameHandler(domain)) // with domain
|
||||
s.Mux.HandleFunc(hostlan, s.subnameHandler(domain)) // with "lan" domain
|
||||
}
|
||||
subnames[host] = ip
|
||||
w.Write([]byte("ok\n"))
|
||||
}
|
||||
|
||||
func (s *Server) SetDNSEntries(dnsEntries []IP) {
|
||||
for _, entry := range dnsEntries {
|
||||
dn := string(entry.Host)
|
||||
if strings.HasSuffix(dn, ".lan") {
|
||||
entry.Host = lcHostname(strings.TrimSuffix(dn, "lan")) + s.domain
|
||||
}
|
||||
s.setSubname(entry)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Server) SetLeases(leases []dhcp4d.Lease) {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
@ -265,16 +367,32 @@ func (s *Server) SetLeases(leases []dhcp4d.Lease) {
|
||||
if l.Hostname == "" {
|
||||
continue
|
||||
}
|
||||
lower := strings.ToLower(l.Hostname)
|
||||
if _, ok := s.hostsByName[lcHostname(lower)]; ok {
|
||||
lower := lcHostname(strings.ToLower(l.Hostname))
|
||||
if _, ok := s.hostsByName[lower]; ok {
|
||||
continue // don’t overwrite e.g. the hostname entry
|
||||
}
|
||||
s.hostsByName[lcHostname(lower)] = l.Addr.String()
|
||||
s.hostsByName[lower] = l.Addr.String()
|
||||
|
||||
subnames, ok := s.subnames[s.domain]
|
||||
if !ok {
|
||||
subnames = make(map[lcHostname]IP)
|
||||
s.subnames[s.domain] = subnames
|
||||
}
|
||||
if l.Addr.To4() != nil {
|
||||
subnames[lower] = IP{
|
||||
IPv4: l.Addr,
|
||||
IPv6: subnames[lower].IPv6,
|
||||
}
|
||||
} else {
|
||||
subnames[lower] = IP{
|
||||
IPv4: subnames[lower].IPv4,
|
||||
IPv6: l.Addr,
|
||||
}
|
||||
}
|
||||
|
||||
if rev, err := dns.ReverseAddr(l.Addr.String()); err == nil {
|
||||
s.hostsByIP[rev] = l.Hostname
|
||||
}
|
||||
s.Mux.HandleFunc(lower+".", s.subnameHandler(lower))
|
||||
s.Mux.HandleFunc(lower+"."+s.domain+".", s.subnameHandler(lower))
|
||||
}
|
||||
}
|
||||
|
||||
@ -329,10 +447,7 @@ func isLocalInAddrArpa(q string) bool {
|
||||
|
||||
var errEmpty = errors.New("no answers")
|
||||
|
||||
func (s *Server) resolve(q dns.Question) (rr dns.RR, err error) {
|
||||
if q.Qclass != dns.ClassINET {
|
||||
return nil, nil
|
||||
}
|
||||
func (s *Server) resolveLocal(q dns.Question) (rr dns.RR, err error) {
|
||||
if strings.ToLower(q.Name) == "localhost." {
|
||||
if q.Qtype == dns.TypeAAAA {
|
||||
return dns.NewRR(q.Name + " 3600 IN AAAA ::1")
|
||||
@ -341,21 +456,9 @@ func (s *Server) resolve(q dns.Question) (rr dns.RR, err error) {
|
||||
return dns.NewRR(q.Name + " 3600 IN A 127.0.0.1")
|
||||
}
|
||||
}
|
||||
if q.Qtype == dns.TypeA ||
|
||||
q.Qtype == dns.TypeAAAA ||
|
||||
q.Qtype == dns.TypeMX {
|
||||
name := strings.TrimSuffix(q.Name, ".")
|
||||
name = strings.TrimSuffix(name, "."+s.domain)
|
||||
if host, ok := s.hostByName(name); ok {
|
||||
if q.Qtype == dns.TypeA {
|
||||
return dns.NewRR(q.Name + " 3600 IN A " + host)
|
||||
}
|
||||
return nil, errEmpty
|
||||
}
|
||||
}
|
||||
if q.Qtype == dns.TypePTR {
|
||||
if host, ok := s.hostByIP(q.Name); ok {
|
||||
return dns.NewRR(q.Name + " 3600 IN PTR " + host + "." + s.domain)
|
||||
return dns.NewRR(q.Name + " 3600 IN PTR " + host + "." + string(s.domain))
|
||||
}
|
||||
if strings.HasSuffix(q.Name, "127.in-addr.arpa.") {
|
||||
return dns.NewRR(q.Name + " 3600 IN PTR localhost.")
|
||||
@ -365,32 +468,33 @@ func (s *Server) resolve(q dns.Question) (rr dns.RR, err error) {
|
||||
}
|
||||
|
||||
func (s *Server) handleInternal(w dns.ResponseWriter, r *dns.Msg) {
|
||||
s.prom.queries.Inc()
|
||||
s.prom.questions.Observe(float64(len(r.Question)))
|
||||
s.prom.upstream.WithLabelValues("local").Inc()
|
||||
s.promInc("local", r)
|
||||
if len(r.Question) != 1 { // TODO: answer all questions we can answer
|
||||
return
|
||||
}
|
||||
rr, err := s.resolve(r.Question[0])
|
||||
rr, err := s.resolveLocal(r.Question[0])
|
||||
if err != nil {
|
||||
if err == errEmpty {
|
||||
m := new(dns.Msg)
|
||||
m.SetReply(r)
|
||||
m.RecursionAvailable = true
|
||||
w.WriteMsg(m)
|
||||
return
|
||||
}
|
||||
log.Fatal(err)
|
||||
log.Fatalf("question %#v: %v", r.Question[0], err)
|
||||
}
|
||||
if rr != nil {
|
||||
m := new(dns.Msg)
|
||||
m.SetReply(r)
|
||||
m.RecursionAvailable = true
|
||||
m.Answer = append(m.Answer, rr)
|
||||
w.WriteMsg(m)
|
||||
return
|
||||
}
|
||||
// Send an authoritative NXDOMAIN for local names:
|
||||
// Send an authoritative NXDOMAIN for local:
|
||||
m := new(dns.Msg)
|
||||
m.SetReply(r)
|
||||
m.RecursionAvailable = true
|
||||
m.SetRcode(r, dns.RcodeNameError)
|
||||
w.WriteMsg(m)
|
||||
}
|
||||
@ -411,10 +515,12 @@ func (s *Server) handleRequest(w dns.ResponseWriter, r *dns.Msg) {
|
||||
return
|
||||
}
|
||||
}
|
||||
if !strings.Contains(strings.TrimSuffix(r.Question[0].Name, "."), ".") {
|
||||
s.subnameHandler(s.domain)(w, r)
|
||||
return
|
||||
}
|
||||
|
||||
s.prom.queries.Inc()
|
||||
s.prom.questions.Observe(float64(len(r.Question)))
|
||||
s.prom.upstream.WithLabelValues("DNS").Inc()
|
||||
s.promInc("DNS", r)
|
||||
|
||||
for idx, u := range s.upstreams() {
|
||||
in, _, err := s.client.Exchange(r, u)
|
||||
@ -436,36 +542,20 @@ func (s *Server) handleRequest(w dns.ResponseWriter, r *dns.Msg) {
|
||||
// DNS has no reply for resolving errors
|
||||
}
|
||||
|
||||
func (s *Server) resolveSubname(hostname string, q dns.Question) (dns.RR, error) {
|
||||
func (s *Server) resolveSubname(domain string, q dns.Question) (dns.RR, error) {
|
||||
if q.Qclass != dns.ClassINET {
|
||||
return nil, nil
|
||||
}
|
||||
if q.Qtype == dns.TypeA ||
|
||||
q.Qtype == dns.TypeAAAA ||
|
||||
q.Qtype == dns.TypeMX {
|
||||
name := strings.TrimSuffix(q.Name, "."+hostname+".")
|
||||
name = strings.TrimSuffix(name, "."+hostname+"."+s.domain+".")
|
||||
|
||||
if lower := strings.ToLower(q.Name); lower == hostname+"." ||
|
||||
lower == hostname+"."+s.domain+"." {
|
||||
host, ok := s.hostByName(hostname)
|
||||
if !ok {
|
||||
// The corresponding DHCP lease might have expired, but this
|
||||
// handler is still installed on the mux.
|
||||
return nil, nil // NXDOMAIN
|
||||
if q.Qtype == dns.TypeA || q.Qtype == dns.TypeAAAA /*|| q.Qtype == dns.TypeMX*/ {
|
||||
name := strings.TrimSuffix(q.Name, ".")
|
||||
name = strings.TrimSuffix(name, "."+string(s.domain)) // trim server domain
|
||||
name = strings.TrimSuffix(name, "."+strings.TrimSuffix(domain, "."+string(s.domain))) // trim function domain
|
||||
if ip, ok := s.subname(domain, name); ok {
|
||||
if q.Qtype == dns.TypeA && ip.IPv4.To4() != nil {
|
||||
return dns.NewRR(q.Name + " 3600 IN A " + ip.IPv4.String())
|
||||
}
|
||||
if q.Qtype == dns.TypeA {
|
||||
return dns.NewRR(q.Name + " 3600 IN A " + host)
|
||||
}
|
||||
return nil, errEmpty
|
||||
}
|
||||
|
||||
if ip, ok := s.subname(hostname, name); ok {
|
||||
if q.Qtype == dns.TypeA && ip.To4() != nil {
|
||||
return dns.NewRR(q.Name + " 3600 IN A " + ip.String())
|
||||
}
|
||||
if q.Qtype == dns.TypeAAAA && ip.To4() == nil {
|
||||
return dns.NewRR(q.Name + " 3600 IN AAAA " + ip.String())
|
||||
if q.Qtype == dns.TypeAAAA && ip.IPv6.To4() == nil && ip.IPv6 != nil {
|
||||
return dns.NewRR(q.Name + " 3600 IN AAAA " + ip.IPv6.String())
|
||||
}
|
||||
return nil, errEmpty
|
||||
}
|
||||
@ -473,33 +563,52 @@ func (s *Server) resolveSubname(hostname string, q dns.Question) (dns.RR, error)
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (s *Server) subnameHandler(hostname string) func(w dns.ResponseWriter, r *dns.Msg) {
|
||||
func (s *Server) promInc(label string, r *dns.Msg) {
|
||||
s.prom.queries.Inc()
|
||||
s.prom.questions.Observe(float64(len(r.Question)))
|
||||
s.prom.upstream.WithLabelValues(label).Inc()
|
||||
}
|
||||
|
||||
func (s *Server) subnameHandler(hostname lcHostname) func(w dns.ResponseWriter, r *dns.Msg) {
|
||||
return func(w dns.ResponseWriter, r *dns.Msg) {
|
||||
if len(r.Question) != 1 { // TODO: answer all questions we can answer
|
||||
s.promInc("local", r)
|
||||
return
|
||||
}
|
||||
rr, err := s.resolveSubname(string(hostname), r.Question[0])
|
||||
|
||||
rr, err := s.resolveSubname(hostname, r.Question[0])
|
||||
if err != nil {
|
||||
s.promInc("local", r)
|
||||
if err == errEmpty {
|
||||
m := new(dns.Msg)
|
||||
m.SetReply(r)
|
||||
m.RecursionAvailable = true
|
||||
w.WriteMsg(m)
|
||||
return
|
||||
}
|
||||
log.Fatalf("question %#v: %v", r.Question[0], err)
|
||||
}
|
||||
if rr != nil {
|
||||
s.promInc("local", r)
|
||||
m := new(dns.Msg)
|
||||
m.SetReply(r)
|
||||
m.RecursionAvailable = true
|
||||
m.Answer = append(m.Answer, rr)
|
||||
w.WriteMsg(m)
|
||||
return
|
||||
}
|
||||
|
||||
// Send an authoritative NXDOMAIN for local names:
|
||||
m := new(dns.Msg)
|
||||
m.SetReply(r)
|
||||
m.SetRcode(r, dns.RcodeNameError)
|
||||
w.WriteMsg(m)
|
||||
if r.Question[0].Qtype == dns.TypePTR || !strings.Contains(strings.TrimSuffix(r.Question[0].Name, "."), ".") || strings.HasSuffix(r.Question[0].Name, ".lan.") {
|
||||
s.promInc("local", r)
|
||||
m := new(dns.Msg)
|
||||
m.SetReply(r)
|
||||
m.RecursionAvailable = true
|
||||
m.SetRcode(r, dns.RcodeNameError)
|
||||
w.WriteMsg(m)
|
||||
return
|
||||
}
|
||||
|
||||
s.handleRequest(w, r)
|
||||
}
|
||||
}
|
||||
|
@ -576,9 +576,9 @@ func TestSubname(t *testing.T) {
|
||||
}
|
||||
})
|
||||
|
||||
setSubname := func(ip, remoteAddr string) {
|
||||
setSubname := func(host, ip, remoteAddr string) {
|
||||
val := url.Values{
|
||||
"host": []string{"sub"},
|
||||
"host": []string{host},
|
||||
"ip": []string{ip},
|
||||
}
|
||||
req := httptest.NewRequest("POST", "/dyndns", strings.NewReader(val.Encode()))
|
||||
@ -593,7 +593,7 @@ func TestSubname(t *testing.T) {
|
||||
}
|
||||
}
|
||||
const ip = "fdf5:3606:2a21:1341:b26e:bfff:fe30:504b"
|
||||
setSubname(ip, "192.168.42.23:1234")
|
||||
setSubname("sub.testtarget", ip, "192.168.42.23:1234")
|
||||
|
||||
for _, name := range []string{
|
||||
"sub.testtarget.lan.",
|
||||
@ -614,7 +614,7 @@ func TestSubname(t *testing.T) {
|
||||
if err := resolveTestTarget(s, hostname+".lan.", net.ParseIP("127.0.0.2")); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
setSubname(ip, "127.0.0.2:1234")
|
||||
setSubname("sub.turin", ip, "127.0.0.2:1234")
|
||||
if err := resolveTestTarget(s, "sub."+hostname+".lan.", net.ParseIP(ip)); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
@ -21,6 +21,7 @@ import (
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strconv"
|
||||
@ -42,6 +43,8 @@ import (
|
||||
|
||||
var log = teelogger.NewConsole()
|
||||
|
||||
var CmdRoot = "/user"
|
||||
|
||||
func subnetMaskSize(mask string) (int, error) {
|
||||
parts := strings.Split(mask, ".")
|
||||
if got, want := len(parts), 4; got != want {
|
||||
@ -729,7 +732,7 @@ func applySysctl(ifname string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func Apply(dir, root string) error {
|
||||
func Apply(dir, root string, firewall bool) error {
|
||||
|
||||
// TODO: split into two parts: delay the up until later
|
||||
if err := applyInterfaces(dir, root); err != nil {
|
||||
@ -757,7 +760,7 @@ func Apply(dir, root string) error {
|
||||
"backupd", // listens on private IPv4/IPv6
|
||||
"captured", // listens on private IPv4/IPv6
|
||||
} {
|
||||
if err := notify.Process("/user/"+process, syscall.SIGUSR1); err != nil {
|
||||
if err := notify.Process(path.Join(CmdRoot, process), syscall.SIGUSR1); err != nil {
|
||||
log.Printf("notifying %s: %v", process, err)
|
||||
}
|
||||
}
|
||||
@ -771,8 +774,10 @@ func Apply(dir, root string) error {
|
||||
appendError(fmt.Errorf("sysctl: %v", err))
|
||||
}
|
||||
|
||||
if err := applyFirewall(dir, ifname); err != nil {
|
||||
appendError(fmt.Errorf("firewall: %v", err))
|
||||
if firewall {
|
||||
if err := applyFirewall(dir, ifname); err != nil {
|
||||
appendError(fmt.Errorf("firewall: %v", err))
|
||||
}
|
||||
}
|
||||
|
||||
if err := applyWireGuard(dir); err != nil {
|
||||
|
@ -1,6 +0,0 @@
|
||||
---
|
||||
title: "{{ replace .Name "-" " " | title }}"
|
||||
date: {{ .Date }}
|
||||
draft: true
|
||||
---
|
||||
|
@ -1,19 +0,0 @@
|
||||
baseURL = "https://router7.org/"
|
||||
languageCode = "en-us"
|
||||
title = "router7"
|
||||
theme = "router7"
|
||||
disableKinds = ["RSS", "taxonomyTerm"]
|
||||
publishDir = "../docs/"
|
||||
enableRobotsTXT = true
|
||||
|
||||
[markup.goldmark.renderer]
|
||||
# Required for e.g. <img> tags in markdown articles.
|
||||
unsafe = true
|
||||
|
||||
[menu]
|
||||
|
||||
[[menu.main]]
|
||||
name = "github"
|
||||
title = "GitHub"
|
||||
url = "https://github.com/rtr7/router7"
|
||||
weight = 60
|
@ -1,37 +0,0 @@
|
||||
---
|
||||
title: "router7: a small home internet router completely written in Go"
|
||||
menu:
|
||||
main:
|
||||
title: "Home"
|
||||
weight: 10
|
||||
---
|
||||
|
||||
# router7
|
||||
|
||||
router7 is a pure-Go implementation of a small home internet router. It comes with all the services required to make a [fiber7 internet connection](https://www.init7.net/en/internet/fiber7/) work (DHCPv4, DHCPv6, DNS, etc.).
|
||||
|
||||
Note that this project should be considered a (working!) tech demo. Feature requests will likely not be implemented, and see [CONTRIBUTING.md](CONTRIBUTING.md) for details about which contributions are welcome.
|
||||
|
||||
## Motivation
|
||||
|
||||
Before starting router7, I was using the [Turris Omnia](https://omnia.turris.cz/en/) router running OpenWrt. That worked fine up until May 2018, when an automated update pulled in a new version of [odhcp6c](https://git.openwrt.org/?p=project/odhcp6c.git;a=shortlog), OpenWrt’s DHCPv6 client. That version is incompatible with fiber7’s DHCP server setup (I think there are shortcomings on both sides).
|
||||
|
||||
It was not only quicker to develop my own router than to wait for either side to resolve the issue, but it was also a lot of fun and allowed me to really tailor my router to my needs, experimenting with a bunch of interesting ideas I had.
|
||||
|
||||
## Project goals
|
||||
|
||||
* Maximize internet connectivity: retain the most recent DHCP configuration across reboots and even after its expiration (chances are the DHCP server will be back before the configuration stops working).
|
||||
* Unit/integration tests use fiber7 packet capture files to minimize the chance of software changes breaking my connectivity.
|
||||
* Safe and quick updates
|
||||
* Auto-rollback of updates which result in loss of connectivity: the diagnostics daemon assesses connectivity state, the update tool reads it and rolls back faulty updates.
|
||||
* Thanks to kexec, updates translate into merely 13s of internet connectivity loss.
|
||||
* Easy debugging
|
||||
* Configuration-related network packets (e.g. DHCP, IPv6 neighbor/router advertisements) are stored in a ring buffer which can be streamed into [Wireshark](https://www.wireshark.org/), allowing for live and retro-active debugging.
|
||||
* The diagnostics daemon performs common diagnostic steps (ping, traceroute, …) for you.
|
||||
* All state in the system is stored as human-readable JSON within the `/perm` partition and can be modified.
|
||||
|
||||
## Hardware
|
||||
|
||||
The reference hardware platform is the [PC Engines™ apu2c4](https://pcengines.ch/apu2c4.htm) system board. It features a 1 GHz quad core amd64 CPU, 4 GB of RAM, 3 Ethernet ports and a DB9 serial port. It conveniently supports PXE boot, the schematics and bootloader sources are available. I recommend the [msata16g](https://pcengines.ch/msata16g.htm) SSD module for reliable persistent storage and the [usbcom1a](https://pcengines.ch/usbcom1a.htm) serial adapter if you don’t have one already.
|
||||
|
||||
Other hardware might work, too, but is not tested.
|
@ -1,64 +0,0 @@
|
||||
---
|
||||
title: "router7: architecture"
|
||||
menu:
|
||||
main:
|
||||
title: "Architecture"
|
||||
weight: 20
|
||||
---
|
||||
|
||||
# Architecture
|
||||
|
||||
router7 is based on [gokrazy](https://gokrazy.org/): it is an appliance which gets packed into a hard disk image, containing a FAT partition with the kernel, a read-only SquashFS partition for the root file system and an ext4 partition for permanent data.
|
||||
|
||||
The individual services can be found in [github.com/rtr7/router7/cmd](https://pkg.go.dev/github.com/rtr7/router7/cmd)
|
||||
|
||||
* Each service runs in a separate process.
|
||||
* Services communicate with each other by persisting state files. E.g., `cmd/dhcp4` writes `/perm/dhcp4/wire/lease.json`.
|
||||
* A service notifies other services about state changes by sending them signal `SIGUSR1`.
|
||||
|
||||
## Configuration files
|
||||
|
||||
{{<table "table table-striped table-bordered">}}
|
||||
| File | Consumer(s) | Purpose |
|
||||
|---|---|---|
|
||||
| `/perm/interfaces.json` | `netconfigd` | Set IP/MAC addresses of `uplink0` and `lan0` |
|
||||
| `/perm/portforwardings.json` | `netconfigd` | Configure nftables port forwarding rules |
|
||||
| `/perm/dhcp6/duid` | `dhcp6` | Set DHCP Unique Identifier (DUID) for obtaining static leases |
|
||||
{{</table>}}
|
||||
|
||||
## State files
|
||||
|
||||
{{<table "table table-striped table-bordered">}}
|
||||
| File | Producer | Consumer(s) | Purpose |
|
||||
|---|---|---|---|
|
||||
| `/perm/dhcp4/wire/ack` | `dhcp4` | `dhcp4` | last DHCPACK packet for renewals across restarts |
|
||||
| `/perm/dhcp4/wire/lease.json` | `dhcp4` | `netconfigd` | Obtained DHCPv4 lease |
|
||||
| `/perm/dhcp6/wire/lease.json` | `dhcp6` | `netconfigd`, `radvd` | Obtained DHCPv6 lease |
|
||||
| `/perm/dhcp4d/leases.json` | `dhcp4d` | `dhcp4d`, `dnsd` | DHCPv4 leases handed out (including hostnames) |
|
||||
{{</table>}}
|
||||
|
||||
## Available ports
|
||||
|
||||
{{<table "table table-striped table-bordered">}}
|
||||
| Port | Purpose |
|
||||
|---|---|
|
||||
| `<public>:8053` | `dnsd` metrics (forwarded requests)
|
||||
| `<public>:8066` | `netconfigd` metrics (nftables counters)
|
||||
| `<private>:80` | gokrazy web interface
|
||||
| `<private>:67` | `dhcp4d`
|
||||
| `<private>:58` | `radvd`
|
||||
| `<private>:53` | `dnsd`
|
||||
| `<private>:8077` | `backupd` (serve backup.tar.gz)
|
||||
| `<private>:7733` | `diagd` (perform diagnostics)
|
||||
| `<private>:5022` | `captured` (serve captured packets)
|
||||
{{</table>}}
|
||||
|
||||
Here’s an example of `cmd/diagd` output:
|
||||
|
||||
<img src="https://github.com/rtr7/router7/raw/master/2018-07-14-diagd.png"
|
||||
width="800" alt="diagd output">
|
||||
|
||||
Here’s an example of `cmd/netconfigd` metrics when scraped with [Prometheus](https://prometheus.io/) and displayed in [Grafana](https://grafana.com/):
|
||||
|
||||
<img src="https://github.com/rtr7/router7/raw/master/2018-07-14-grafana.png"
|
||||
width="800" alt="metrics in grafana">
|
@ -1,93 +0,0 @@
|
||||
---
|
||||
title: "router7: installation"
|
||||
menu:
|
||||
main:
|
||||
title: "Installation"
|
||||
weight: 30
|
||||
---
|
||||
|
||||
# Installation
|
||||
|
||||
Connect your serial adapter ([usbcom1a](https://pcengines.ch/usbcom1a.htm) works well if you don’t have one already) to the apu2c4 and start a program to use it, e.g. `screen /dev/ttyUSB0 115200`. Then, power on the apu2c4 and configure it to do PXE boot:
|
||||
|
||||
* Press `F10` to enter the boot menu
|
||||
* Press `3` to enter setup
|
||||
* Press `n` to enable network boot
|
||||
* Press `c` to move mSATA to the top of the boot order
|
||||
* Press `e` to move iPXE to the top of the boot order
|
||||
* Press `s` to save configuration and exit
|
||||
|
||||
Connect a network cable on `net0`, the port closest to the serial console port:
|
||||
|
||||
<img src="https://github.com/rtr7/router7/raw/master/devsetup.jpg"
|
||||
width="800" alt="router7 development setup">
|
||||
|
||||
Next, build a router7 image:
|
||||
|
||||
```shell
|
||||
go get -u github.com/gokrazy/tools/cmd/gokr-packer github.com/rtr7/tools/cmd/...
|
||||
go get -u -d github.com/rtr7/router7
|
||||
mkdir /tmp/recovery
|
||||
GOARCH=amd64 gokr-packer \
|
||||
-hostname=router7 \
|
||||
-overwrite_boot=/tmp/recovery/boot.img \
|
||||
-overwrite_mbr=/tmp/recovery/mbr.img \
|
||||
-overwrite_root=/tmp/recovery/root.img \
|
||||
-kernel_package=github.com/rtr7/kernel \
|
||||
-firmware_package=github.com/rtr7/kernel \
|
||||
-gokrazy_pkgs=github.com/gokrazy/gokrazy/cmd/ntp \
|
||||
-serial_console=ttyS0,115200n8 \
|
||||
github.com/rtr7/router7/cmd/...
|
||||
```
|
||||
|
||||
Run `rtr7-recover -boot=/tmp/recovery/boot.img -mbr=/tmp/recovery/mbr.img -root=/tmp/recovery/root.img` to:
|
||||
|
||||
* trigger a reset [if a Teensy with the rebootor firmware is attached](#rebootor)
|
||||
* serve a DHCP lease to all clients which request PXE boot (i.e., your apu2c4)
|
||||
* serve via TFTP:
|
||||
* the PXELINUX bootloader
|
||||
* the router7 kernel
|
||||
* an initrd archive containing the rtr7-recovery-init program and mke2fs
|
||||
* serve via HTTP the boot and root images
|
||||
* optionally serve via HTTP a backup.tar.gz image containing files for `/perm` (e.g. for moving to new hardware, rolling back corrupted state, or recovering from a disk failure)
|
||||
* exit once the router successfully wrote the images to disk
|
||||
|
||||
## Updates
|
||||
|
||||
Run e.g. `rtr7-safe-update -updates_dir=$HOME/router7/updates` to:
|
||||
|
||||
* verify the router currently has connectivity, abort the update otherwise
|
||||
* download a backup archive of `/perm`
|
||||
* build a new image
|
||||
* update the router
|
||||
* wait until the router restored connectivity, roll back the update using `rtr7-recover` otherwise
|
||||
|
||||
The update step uses kexec to reduce the downtime to approximately 15 seconds.
|
||||
|
||||
## Manual Recovery
|
||||
|
||||
Given `rtr7-safe-update`’s safeguards, manual recovery should rarely be required.
|
||||
|
||||
To manually roll back to an older image, invoke `rtr7-safe-update` via the
|
||||
`recover.bash` script in the image directory underneath `-updates_dir`, e.g.:
|
||||
|
||||
```shell
|
||||
% cd ~/router7/updates/2018-07-03T17:33:52+02:00
|
||||
% ./recover.bash
|
||||
```
|
||||
|
||||
## Teensy rebootor {#rebootor}
|
||||
|
||||
The cheap and widely-available [Teensy++ USB development board](https://www.pjrc.com/store/teensypp.html) comes with a firmware called rebootor, which is used by the [`teensy_loader_cli`](https://www.pjrc.com/teensy/loader_cli.html) program to perform hard resets.
|
||||
|
||||
This setup can be used to programmatically reset the apu2c4 (from `rtr7-recover`) by connecting the Teensy++ to the [apu2c4’s reset pins](http://pcengines.ch/pdf/apu2.pdf):
|
||||
* connect the Teensy++’s `GND` pin to the apu2c4 J2’s pin 4 (`GND`)
|
||||
* connect the Teensy++’s `B7` pin to the apu2c4 J2’s pin 5 (`3.3V`, resets when pulled to `GND`)
|
||||
|
||||
You can find a working rebootor firmware .hex file at https://github.com/PaulStoffregen/teensy_loader_cli/issues/38
|
||||
|
||||
## Prometheus
|
||||
|
||||
See https://github.com/rtr7/router7/tree/master/contrib/prometheus for example
|
||||
configuration files, and install the [router7 Grafana
|
||||
Dashboard](https://grafana.com/dashboards/8288).
|
@ -1,2 +0,0 @@
|
||||
User-Agent: *
|
||||
sitemap: https://router7.org/sitemap.xml
|
@ -1,6 +0,0 @@
|
||||
{{ $htmlTable := .Inner | markdownify }}
|
||||
{{ $class := .Get 0 }}
|
||||
{{ $old := "<table>" }}
|
||||
{{ $new := printf "<table class=\"%s\">" $class }}
|
||||
{{ $htmlTable := replace $htmlTable $old $new }}
|
||||
{{ $htmlTable | safeHTML }}
|
@ -1,19 +0,0 @@
|
||||
.bd-toc {
|
||||
position: sticky;
|
||||
top: 4rem;
|
||||
height: calc(100vh - 4rem);
|
||||
overflow-y: auto; }
|
||||
|
||||
.bd-toc ul {
|
||||
list-style: none;
|
||||
padding-left: 1em;
|
||||
border-left: 1px solid #eee; }
|
||||
|
||||
.bd-toc li {
|
||||
margin-top: 1em;
|
||||
margin-bottom: 1em; }
|
||||
|
||||
/* TODO: move this to a separate style sheet */
|
||||
.bigbutton {
|
||||
margin-left: 1em;
|
||||
margin-right: 1em; }
|
@ -1 +0,0 @@
|
||||
{"Target":"sass/sidebar.css","MediaType":"text/css","Data":{}}
|
@ -1 +0,0 @@
|
||||
router7.org
|
7
website/static/bootstrap-4.4.1.min.css
vendored
7
website/static/bootstrap-4.4.1.min.css
vendored
File diff suppressed because one or more lines are too long
7
website/static/bootstrap-4.4.1.min.js
vendored
7
website/static/bootstrap-4.4.1.min.js
vendored
File diff suppressed because one or more lines are too long
2
website/static/jquery-3.4.1.slim.min.js
vendored
2
website/static/jquery-3.4.1.slim.min.js
vendored
File diff suppressed because one or more lines are too long
5
website/static/popper-1.16.0.min.js
vendored
5
website/static/popper-1.16.0.min.js
vendored
File diff suppressed because one or more lines are too long
@ -1,20 +0,0 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2020 YOUR_NAME_HERE
|
||||
|
||||
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.
|
@ -1,2 +0,0 @@
|
||||
+++
|
||||
+++
|
@ -1,23 +0,0 @@
|
||||
.bd-toc {
|
||||
position: sticky;
|
||||
top: 4rem;
|
||||
height: calc(100vh - 4rem);
|
||||
overflow-y: auto
|
||||
}
|
||||
|
||||
.bd-toc ul {
|
||||
list-style: none;
|
||||
padding-left: 1em;
|
||||
border-left: 1px solid #eee;
|
||||
}
|
||||
|
||||
.bd-toc li {
|
||||
margin-top: 1em;
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
|
||||
/* TODO: move this to a separate style sheet */
|
||||
.bigbutton {
|
||||
margin-left: 1em;
|
||||
margin-right: 1em;
|
||||
}
|
@ -1,17 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
{{- partial "head.html" . -}}
|
||||
<body>
|
||||
<div id="content">
|
||||
<div class="container">
|
||||
|
||||
{{- partial "header.html" . -}}
|
||||
|
||||
{{ block "main" . }}
|
||||
{{ end }}
|
||||
|
||||
</div>
|
||||
</div>
|
||||
{{- partial "footer.html" . -}}
|
||||
</body>
|
||||
</html>
|
@ -1,31 +0,0 @@
|
||||
{{ define "main" }}
|
||||
<div class="row">
|
||||
<div class="col-md-10">
|
||||
|
||||
{{- partial "nav.html" . -}}
|
||||
{{ .Content }}
|
||||
|
||||
<h1>list template</h1>
|
||||
|
||||
<ul>
|
||||
{{ range .Pages }}
|
||||
<li>
|
||||
<a href="{{ .Permalink }}">{{ .Title }}</a>
|
||||
</li>
|
||||
{{ end }}
|
||||
</ul>
|
||||
|
||||
<hr>
|
||||
|
||||
<p class="small">
|
||||
© 2018 Michael Stapelberg and contributors
|
||||
</p>
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
<aside class="bd-toc">
|
||||
{{ .TableOfContents }}
|
||||
</aside>
|
||||
</div>
|
||||
</div>
|
||||
{{ end }}
|
||||
|
@ -1,20 +0,0 @@
|
||||
{{ define "main" }}
|
||||
<div class="row">
|
||||
<div class="col-md-10">
|
||||
|
||||
{{- partial "nav.html" . -}}
|
||||
{{ .Content }}
|
||||
|
||||
<hr>
|
||||
|
||||
<p class="small">
|
||||
© 2018 Michael Stapelberg and contributors
|
||||
</p>
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
<aside class="bd-toc">
|
||||
{{ .TableOfContents }}
|
||||
</aside>
|
||||
</div>
|
||||
</div>
|
||||
{{ end }}
|
@ -1,17 +0,0 @@
|
||||
{{ define "main" }}
|
||||
<div class="row">
|
||||
<div class="col-md-10">
|
||||
|
||||
{{- partial "nav.html" . -}}
|
||||
{{ .Content }}
|
||||
|
||||
<hr>
|
||||
|
||||
<p class="small">
|
||||
© 2018 Michael Stapelberg and contributors
|
||||
</p>
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
</div>
|
||||
</div>
|
||||
{{ end }}
|
@ -1,7 +0,0 @@
|
||||
<!-- Optional JavaScript -->
|
||||
<!-- jQuery first, then Popper.js, then Bootstrap JS -->
|
||||
<script src="/jquery-3.4.1.slim.min.js" integrity="sha384-J6qa4849blE2+poT4WnyKhv5vZF5SrPo0iEjwBvKU7imGFAV0wwj1yYfoRSJoZ+n" crossorigin="anonymous"></script>
|
||||
<script src="/popper-1.16.0.min.js" integrity="sha384-Q6E9RHvbIyZFJoft+2mJbHaEWldlvI9IOYy5n3zV9zzTtmI3UksdQRVvoxMfooAo" crossorigin="anonymous"></script>
|
||||
<script src="/bootstrap-4.4.1.min.js" integrity="sha384-wfSDF2E50Y2D1uUdj0O3uMBJnjuUD4Ih7YwaYd1iqfktj0Uod8GCExl3Og8ifwB6" crossorigin="anonymous"></script>
|
||||
</body>
|
||||
</html>
|
@ -1,14 +0,0 @@
|
||||
<head>
|
||||
<!-- Required meta tags -->
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
||||
|
||||
<!-- Bootstrap CSS -->
|
||||
<link rel="stylesheet" href="/bootstrap-4.4.1.min.css" crossorigin="anonymous">
|
||||
|
||||
{{ $sass := resources.Get "sass/sidebar.scss" }}
|
||||
{{ $style := $sass | resources.ToCSS }}
|
||||
<link rel="stylesheet" href="{{ $style.Permalink }}">
|
||||
|
||||
<title>{{ .Title }}</title>
|
||||
</head>
|
@ -1,15 +0,0 @@
|
||||
<nav class="navbar navbar-expand-lg navbar-light bg-light">
|
||||
<a class="navbar-brand" href="#">router7</a>
|
||||
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarNavAltMarkup" aria-controls="navbarNavAltMarkup" aria-expanded="false" aria-label="Toggle navigation">
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
</button>
|
||||
<div class="collapse navbar-collapse" id="navbarNavAltMarkup">
|
||||
<div class="navbar-nav ml-auto">
|
||||
{{ $current := . }}
|
||||
{{ range .Site.Menus.main }}
|
||||
{{ $active := $current.IsMenuCurrent "main" . }}
|
||||
<a class="nav-item nav-link {{ if $active }}active{{ end }}" href="{{ .URL }}">{{ .Title }} {{ if $active }}<span class="sr-only">(current)</span>{{ end }}</a>
|
||||
{{ end }}
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
@ -1,21 +0,0 @@
|
||||
# theme.toml template for a Hugo theme
|
||||
# See https://github.com/gohugoio/hugoThemes#themetoml for an example
|
||||
|
||||
name = "Distri"
|
||||
license = "MIT"
|
||||
licenselink = "https://github.com/yourname/yourtheme/blob/master/LICENSE"
|
||||
description = ""
|
||||
homepage = "http://example.com/"
|
||||
tags = []
|
||||
features = []
|
||||
min_version = "0.41.0"
|
||||
|
||||
[author]
|
||||
name = ""
|
||||
homepage = ""
|
||||
|
||||
# If porting an existing theme
|
||||
[original]
|
||||
name = ""
|
||||
homepage = ""
|
||||
repo = ""
|
Reference in New Issue
Block a user