172 lines
4.2 KiB
Go
172 lines
4.2 KiB
Go
// Copyright 2018 Google Inc.
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
// Binary netconfigd reads state from dhcp4, dhcp6, … and applies it.
|
|
package main
|
|
|
|
import (
|
|
"flag"
|
|
"net"
|
|
"net/http"
|
|
"os"
|
|
"os/signal"
|
|
"sync"
|
|
"syscall"
|
|
|
|
"github.com/gokrazy/gokrazy"
|
|
"github.com/google/nftables"
|
|
"github.com/prometheus/client_golang/prometheus"
|
|
"github.com/prometheus/client_golang/prometheus/promauto"
|
|
"github.com/prometheus/client_golang/prometheus/promhttp"
|
|
|
|
"github.com/rtr7/router7/internal/multilisten"
|
|
"github.com/rtr7/router7/internal/netconfig"
|
|
"github.com/rtr7/router7/internal/notify"
|
|
"github.com/rtr7/router7/internal/teelogger"
|
|
)
|
|
|
|
var log = teelogger.NewConsole()
|
|
|
|
var (
|
|
linger = flag.Bool("linger", true, "linger around after applying the configuration (until killed)")
|
|
)
|
|
|
|
func init() {
|
|
var c nftables.Conn
|
|
for _, metric := range []struct {
|
|
name string
|
|
labels prometheus.Labels
|
|
obj *nftables.CounterObj
|
|
packets, bytes uint64
|
|
}{
|
|
{
|
|
name: "filter_forward",
|
|
labels: prometheus.Labels{"family": "ipv4"},
|
|
obj: &nftables.CounterObj{
|
|
Table: &nftables.Table{Family: nftables.TableFamilyIPv4, Name: "filter"},
|
|
Name: "fwded",
|
|
},
|
|
},
|
|
{
|
|
name: "filter_forward",
|
|
labels: prometheus.Labels{"family": "ipv6"},
|
|
obj: &nftables.CounterObj{
|
|
Table: &nftables.Table{Family: nftables.TableFamilyIPv6, Name: "filter"},
|
|
Name: "fwded",
|
|
},
|
|
},
|
|
} {
|
|
metric := metric // copy
|
|
var mu sync.Mutex
|
|
updateCounter := func() {
|
|
mu.Lock()
|
|
defer mu.Unlock()
|
|
objs, err := c.GetObjReset(metric.obj)
|
|
if err != nil ||
|
|
len(objs) != 1 {
|
|
return
|
|
}
|
|
if co, ok := objs[0].(*nftables.CounterObj); ok {
|
|
metric.packets += co.Packets
|
|
metric.bytes += co.Bytes
|
|
}
|
|
}
|
|
promauto.NewCounterFunc(
|
|
prometheus.CounterOpts{
|
|
Subsystem: "nftables",
|
|
Name: metric.name + "_packets",
|
|
Help: "packet count",
|
|
ConstLabels: metric.labels,
|
|
},
|
|
func() float64 {
|
|
updateCounter()
|
|
return float64(metric.packets)
|
|
})
|
|
promauto.NewCounterFunc(
|
|
prometheus.CounterOpts{
|
|
Subsystem: "nftables",
|
|
Name: metric.name + "_bytes",
|
|
Help: "bytes count",
|
|
ConstLabels: metric.labels,
|
|
},
|
|
func() float64 {
|
|
updateCounter()
|
|
return float64(metric.bytes)
|
|
})
|
|
}
|
|
}
|
|
|
|
var httpListeners = multilisten.NewPool()
|
|
|
|
func updateListeners() error {
|
|
hosts, err := gokrazy.PrivateInterfaceAddrs()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if net1, err := multilisten.IPv6Net1("/perm"); err == nil {
|
|
hosts = append(hosts, net1)
|
|
}
|
|
|
|
httpListeners.ListenAndServe(hosts, func(host string) multilisten.Listener {
|
|
return &http.Server{Addr: net.JoinHostPort(host, "8066")}
|
|
})
|
|
return nil
|
|
}
|
|
|
|
func logic() error {
|
|
if *linger {
|
|
http.Handle("/metrics", promhttp.Handler())
|
|
if err := updateListeners(); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
ch := make(chan os.Signal, 1)
|
|
signal.Notify(ch, syscall.SIGUSR1)
|
|
for {
|
|
err := netconfig.Apply("/perm/", "/")
|
|
|
|
// 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 {
|
|
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 {
|
|
log.Printf("kill -HUP 1: %v", err)
|
|
}
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if !*linger {
|
|
break
|
|
}
|
|
<-ch
|
|
if err := updateListeners(); err != nil {
|
|
log.Printf("updateListeners: %v", err)
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func main() {
|
|
flag.Parse()
|
|
if err := logic(); err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
}
|